diff --git a/app/src-tauri/src/claude_code.rs b/app/src-tauri/src/claude_code.rs index c70f960cc0..7ff7a98ed3 100644 --- a/app/src-tauri/src/claude_code.rs +++ b/app/src-tauri/src/claude_code.rs @@ -43,7 +43,7 @@ end tell"#; .args(["-e", script]) .spawn() .map_err(|e| format!("failed to open Terminal.app: {e}"))?; - return Ok("Terminal.app".into()); + Ok("Terminal.app".into()) } #[cfg(target_os = "linux")] diff --git a/app/src-tauri/src/imessage_scanner/mod.rs b/app/src-tauri/src/imessage_scanner/mod.rs index 289fd7cac0..e8cefd9929 100644 --- a/app/src-tauri/src/imessage_scanner/mod.rs +++ b/app/src-tauri/src/imessage_scanner/mod.rs @@ -372,7 +372,7 @@ fn extract_text_from_attributed_body(blob: &[u8]) -> Option { .filter_map(|r| String::from_utf8(r).ok()) .filter(|s| { let trimmed = s.trim(); - trimmed.len() >= 2 && !ignored_markers.iter().any(|m| trimmed == *m) + trimmed.len() >= 2 && !ignored_markers.contains(&trimmed) }) .max_by_key(|s| s.len()) .map(|s| s.trim().to_string()) diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs index ee15b7897e..b597458325 100644 --- a/app/src-tauri/src/lib.rs +++ b/app/src-tauri/src/lib.rs @@ -1195,7 +1195,7 @@ fn mascot_window_show(app: AppHandle) -> Result<(), String> { log::info!("[mascot-window] show requested"); #[cfg(target_os = "macos")] { - return mascot_native_window::show(&app); + mascot_native_window::show(&app) } #[cfg(not(target_os = "macos"))] { @@ -1258,11 +1258,11 @@ fn notch_window_show(app: AppHandle) -> Result<(), String> { log::info!("[notch-window] show requested"); #[cfg(target_os = "macos")] { - return dispatch_notch_on_main(app, |app| { + dispatch_notch_on_main(app, |app| { if let Err(e) = notch_window::show(app) { log::warn!("[notch-window] show failed: {e}"); } - }); + }) } #[cfg(not(target_os = "macos"))] { @@ -1277,7 +1277,7 @@ fn notch_window_hide(app: AppHandle) -> Result<(), String> { log::info!("[notch-window] hide requested"); #[cfg(target_os = "macos")] { - return dispatch_notch_on_main(app, |_app| notch_window::hide()); + dispatch_notch_on_main(app, |_app| notch_window::hide()) } #[cfg(not(target_os = "macos"))] { diff --git a/app/src-tauri/src/mascot_native_window.rs b/app/src-tauri/src/mascot_native_window.rs index 063361a356..5c27a36ea2 100644 --- a/app/src-tauri/src/mascot_native_window.rs +++ b/app/src-tauri/src/mascot_native_window.rs @@ -52,6 +52,9 @@ const DRAG_POLL_SECONDS: f64 = 0.016; /// dropped webview. struct MascotPanel { panel: Retained, + // RAII keep-alive: never read, but dropping it deallocates the WKWebView and + // blanks the panel. Must outlive the show/hide cycle — do not remove. + #[allow(dead_code)] webview: Retained, drag_timer: Retained, } @@ -389,7 +392,7 @@ unsafe fn build_webview( let _: () = msg_send![&*webview, setAutoresizingMask: 18u64]; // width|height // Make the webview the panel's content view so it fills the frame. - let webview_ref: &objc2::runtime::AnyObject = &*webview; + let webview_ref: &objc2::runtime::AnyObject = &webview; let webview_view: *mut objc2::runtime::AnyObject = webview_ref as *const _ as *mut objc2::runtime::AnyObject; let _: () = msg_send![panel, setContentView: webview_view]; diff --git a/app/src-tauri/src/native_notifications/mod.rs b/app/src-tauri/src/native_notifications/mod.rs index 7fb5281e7d..18e23669a7 100644 --- a/app/src-tauri/src/native_notifications/mod.rs +++ b/app/src-tauri/src/native_notifications/mod.rs @@ -37,7 +37,7 @@ use crate::AppRuntime; pub fn notification_permission_state() -> Result { #[cfg(target_os = "macos")] { - return macos::permission_state(); + macos::permission_state() } #[cfg(not(target_os = "macos"))] { @@ -51,7 +51,7 @@ pub fn notification_permission_state() -> Result { pub fn notification_permission_request() -> Result { #[cfg(target_os = "macos")] { - return macos::request_permission(); + macos::request_permission() } #[cfg(not(target_os = "macos"))] { diff --git a/app/src-tauri/src/notch_window.rs b/app/src-tauri/src/notch_window.rs index 78653688cb..4fc3f7202a 100644 --- a/app/src-tauri/src/notch_window.rs +++ b/app/src-tauri/src/notch_window.rs @@ -61,10 +61,6 @@ thread_local! { static NOTCH: RefCell> = const { RefCell::new(None) }; } -pub(crate) fn is_open() -> bool { - NOTCH.with(|cell| cell.borrow().is_some()) -} - pub(crate) fn hide() { NOTCH.with(|cell| { if let Some(existing) = cell.borrow_mut().take() { @@ -262,7 +258,7 @@ unsafe fn build_webview( // Auto-resize to fill the panel content view. let _: () = msg_send![&*webview, setAutoresizingMask: 18u64]; // width|height - let webview_ref: &objc2::runtime::AnyObject = &*webview; + let webview_ref: &objc2::runtime::AnyObject = &webview; let webview_view = webview_ref as *const _ as *mut objc2::runtime::AnyObject; let _: () = msg_send![panel, setContentView: webview_view]; diff --git a/app/src/services/chatService.ts b/app/src/services/chatService.ts index 6fb128bb5c..f6b25e34d6 100644 --- a/app/src/services/chatService.ts +++ b/app/src/services/chatService.ts @@ -966,7 +966,7 @@ export function subscribeChatEvents(listeners: ChatEventListeners): () => void { } // Artifact lifecycle events (#2779). The Rust subscriber in - // `channels/providers/web::ArtifactSurfaceSubscriber` packs the + // `web_chat::ArtifactSurfaceSubscriber` packs the // artifact payload into the generic `args` field of the wire // envelope (kept the WebChannelEvent struct shape stable to avoid // touching ~10 existing call sites with `..Default::default()`). diff --git a/docs/TEST-COVERAGE-MATRIX.md b/docs/TEST-COVERAGE-MATRIX.md index 171ad792a9..51b9a18b1a 100644 --- a/docs/TEST-COVERAGE-MATRIX.md +++ b/docs/TEST-COVERAGE-MATRIX.md @@ -182,7 +182,7 @@ Canonical mapping of every product feature to its test source(s). Drives gap-fil | 4.2.1 | User Message Handling | WD+RI | `conversations-web-channel-flow.spec.ts`, `tests/json_rpc_e2e.rs` | ✅ | | | 4.2.2 | AI Response Generation | WD | `agent-review.spec.ts` | ✅ | Mock LLM | | 4.2.3 | Streaming Responses | RI | `tests/json_rpc_e2e.rs`, `tests/agent_harness_e2e.rs` | ✅ | `tests/agent_harness_e2e.rs` adds provider-level SSE tool-arg accumulation (chunked args reassembled + parsed) and engine-level delta forwarding (#3471) | -| 4.2.4 | Parallel inference (cross-thread + within-thread forked turns) | RU+VU | `src/openhuman/channels/providers/web_tests.rs`, `app/src/store/__tests__/chatRuntimeSlice.test.ts`, `app/src/providers/__tests__/ChatRuntimeProvider.test.tsx` | 🟡 | Concurrent same-/cross-thread dispatch, cooperative `CancellationToken` teardown, and parallel-lane stream routing covered; dedicated WD E2E is a follow-up | +| 4.2.4 | Parallel inference (cross-thread + within-thread forked turns) | RU+VU | `src/openhuman/web_chat/web_tests.rs`, `app/src/store/__tests__/chatRuntimeSlice.test.ts`, `app/src/providers/__tests__/ChatRuntimeProvider.test.tsx` | 🟡 | Concurrent same-/cross-thread dispatch, cooperative `CancellationToken` teardown, and parallel-lane stream routing covered; dedicated WD E2E is a follow-up | | 4.2.5 | Per-thread todo list (plan strip above composer) | RU+VU+WD | `src/openhuman/agent/tools/todo.rs`, `app/src/pages/conversations/components/ThreadTodoStrip.test.tsx`, `app/test/e2e/specs/chat-thread-todo-strip.spec.ts` | ✅ | Read-only thread-scoped todo strip fed by `task_board_updated`; agent `todo` tool guidance + thread binding; E2E drives a `todo` tool call and asserts the card renders | | 4.2.6 | Background-activity panel (chat-header Background tasks button) | VU+WD | `app/src/pages/conversations/hooks/useBackgroundActivity.test.ts`, `app/src/pages/conversations/components/__tests__/BackgroundActivityRows.test.tsx`, `app/test/e2e/specs/chat-background-activity-panel.spec.ts` | ✅ | View-only panel surfacing this chat's async sub-agents + global cron jobs, subconscious/heartbeat status, and memory syncing; freshness-only "Syncing now" labeling; E2E opens the panel and asserts its sections render and close | | 4.2.7 | Plan-mode review (Approve / Reject / Send-feedback before execute) | RU+RI+VU | `src/openhuman/plan_review/gate.rs`, `src/openhuman/plan_review/tool.rs`, `src/openhuman/plan_review/schemas.rs`, `tests/json_rpc_e2e.rs`, `app/src/pages/conversations/components/PlanReviewCard.test.tsx`, `app/src/pages/__tests__/Conversations.render.test.tsx` | ✅ | Interactive turns call `request_plan_review`, which parks the LIVE turn on the in-memory `PlanReviewGate` (oneshot) until the user decides; `plan_review_request` socket event drives `PlanReviewCard`, which resolves via `openhuman.plan_review_decide` (approve resumes-and-executes / reject resumes-and-stops / revise resumes-with-feedback). RU covers gate park/resolve/timeout + tool auto-approve + parking; RI covers the decide RPC; VU covers the card + provider wiring. WD E2E (agent-driven park flow) tracked as follow-up | diff --git a/src/core/all.rs b/src/core/all.rs index d686517cca..8ead22c1df 100644 --- a/src/core/all.rs +++ b/src/core/all.rs @@ -388,7 +388,7 @@ fn build_registered_controllers() -> Vec { push( &mut controllers, DomainGroup::Channels, - crate::openhuman::channels::providers::web::all_web_channel_registered_controllers(), + crate::openhuman::web_chat::all_web_channel_registered_controllers(), ); push( &mut controllers, diff --git a/src/core/event_bus/events.rs b/src/core/event_bus/events.rs index 2d2ee80fe7..bc9a7af811 100644 --- a/src/core/event_bus/events.rs +++ b/src/core/event_bus/events.rs @@ -579,7 +579,7 @@ pub enum DomainEvent { /// flow-approval-surface, PR2/PR3). Unlike `ApprovalRequested`, this /// event carries no `thread_id`/`client_id` — a flow run has neither, so /// the generic chat-routed socket bridge - /// (`channels::providers::web::event_bus::ApprovalSurfaceSubscriber`) + /// (`web_chat::event_bus::ApprovalSurfaceSubscriber`) /// silently drops it (that gap was the original silent-deadlock bug). /// Published by `ApprovalGate::intercept_audited` alongside the existing /// `ApprovalRequested`, bridged by `core::socketio` directly to a @@ -611,7 +611,7 @@ pub enum DomainEvent { /// /// Bridged to the `external_transfer_pending` web-channel socket event by /// `EgressSurfaceSubscriber` (defined in - /// `src/openhuman/channels/providers/web/event_bus.rs`) when the emitting + /// `src/openhuman/web_chat/event_bus.rs`) when the emitting /// turn carries chat routing. `thread_id` / `client_id` come from the /// ambient `APPROVAL_CHAT_CONTEXT` and are `None` for CLI / cron / /// background transfers (no chat surface to route to). diff --git a/src/core/jsonrpc.rs b/src/core/jsonrpc.rs index 8bcabec2f3..fca8e65268 100644 --- a/src/core/jsonrpc.rs +++ b/src/core/jsonrpc.rs @@ -1479,7 +1479,7 @@ async fn events_handler( } let client_id = query.client_id; - let rx = crate::openhuman::channels::providers::web::subscribe_web_channel_events(); + let rx = crate::openhuman::web_chat::subscribe_web_channel_events(); let stream = tokio_stream::wrappers::BroadcastStream::new(rx).filter_map( move |item| -> Option> { let event = match item { @@ -2350,18 +2350,18 @@ pub async fn bootstrap_core_runtime( // `start_channels` is skipped for web-chat-only cores. Without this an // unguarded standalone/CLI/Docker core would park a plan review that never // reaches the UI and dies at the gate TTL. Idempotent (Once-guarded). - crate::openhuman::channels::providers::web::register_approval_surface_subscriber(); + crate::openhuman::web_chat::register_approval_surface_subscriber(); // Bridge emergency-stop halt/resume → the `automation_halt` broadcast on the // same always-run boot path. `start_channels` (which also registers this) // is skipped on a web-chat-only desktop with no listening integrations, so // without this a halt/resume initiated from the CLI or another client would // never reach the UI. Idempotent (Once-guarded). (#4255) - crate::openhuman::channels::providers::web::register_automation_halt_subscriber(); + crate::openhuman::web_chat::register_automation_halt_subscriber(); // Egress-surface bridge (privacy epic S2, #4436) — registered // unconditionally alongside the approval surface so external-transfer // disclosures reach the UI even on cores that skip `start_channels` or run // with the approval gate disabled. Idempotent (OnceLock-guarded). - crate::openhuman::channels::providers::web::register_egress_surface_subscriber(); + crate::openhuman::web_chat::register_egress_surface_subscriber(); if decision.install_gate { // Per-launch correlation token for the approval gate. This is @@ -2382,7 +2382,7 @@ pub async fn bootstrap_core_runtime( ); // (The approval/plan-review surface bridge is registered unconditionally // above — it must run even when this gate-install branch is skipped.) - crate::openhuman::channels::providers::web::register_artifact_surface_subscriber(); + crate::openhuman::web_chat::register_artifact_surface_subscriber(); } else { log::error!( "[runtime] approval gate DISABLED (OPENHUMAN_APPROVAL_GATE=0 honored on host={}) — \ @@ -2402,7 +2402,7 @@ pub async fn bootstrap_core_runtime( // `if approval_gate` block so artifact events still publish when the user // sets OPENHUMAN_APPROVAL_GATE=0 (CR #3328947323 on PR #3026). Idempotent // (OnceLock-guarded inside register_artifact_surface_subscriber). - crate::openhuman::channels::providers::web::register_artifact_surface_subscriber(); + crate::openhuman::web_chat::register_artifact_surface_subscriber(); // --- Workspace migrations -------------------------------------------- crate::openhuman::startup::run_workspace_migrations(&workspace_dir); diff --git a/src/core/observability.rs b/src/core/observability.rs index d81d5b529e..1ae514c17d 100644 --- a/src/core/observability.rs +++ b/src/core/observability.rs @@ -211,7 +211,7 @@ pub enum ExpectedErrorKind { /// `agent::run_single` already suppresses the **agent-layer** Sentry /// event for this condition via the typed /// `AgentError::EmptyProviderResponse` + `AgentError::skips_sentry()` - /// (PR #2790, TAURI-RUST-4JX). But `channels::providers::web:: + /// (PR #2790, TAURI-RUST-4JX). But `web_chat:: /// run_chat_task` **re-reports** the same failure under /// `domain=web_channel operation=run_chat_task` after the typed error /// has been flattened to a `String` at the native-bus boundary — so the @@ -302,7 +302,7 @@ pub enum ExpectedErrorKind { /// (`api_error`) already demotes its own per-attempt event; this catches /// the **re-report** when the same flattened error is raised again under /// `domain=web_channel` / `agent` (the path - /// `channels::providers::web::run_chat_task` → + /// `web_chat::run_chat_task` → /// `report_error_or_expected`). The FE surfaces actionable copy via /// `classify_inference_error`; Sentry must not double-report (F2/F4). /// @@ -975,7 +975,7 @@ fn is_memory_store_breaker_open(lower: &str) -> bool { /// - `"OpenHuman API error (401 Unauthorized): {…\"Session expired. Please /// log in again.\"…}"` — emitted by `providers::ops::api_error` from the /// OpenHuman backend and re-raised through `agent::run_single` / -/// `channels::providers::web::run_chat_task` (OPENHUMAN-TAURI-26). The +/// `web_chat::run_chat_task` (OPENHUMAN-TAURI-26). The /// `"session expired"` substring anchors the match to the OpenHuman /// backend's session-renewal body, not the bare numeric status. /// - `"OpenHuman API error (401 Unauthorized): {…\"error\":\"Invalid token\"…}"` @@ -1870,7 +1870,7 @@ fn is_filesystem_user_path_invalid_message(lower: &str) -> bool { /// `text_chars=0 thinking_chars=0 tool_calls=0`. /// /// This catches the **web-channel re-report** (Sentry TAURI-RUST-4Z1): -/// `channels::providers::web::run_chat_task` wraps the failure as +/// `web_chat::run_chat_task` wraps the failure as /// `"run_chat_task failed client_id=… error=The model returned an empty /// response. Please try again."` and routes it through /// `report_error_or_expected` after the typed @@ -2598,7 +2598,7 @@ fn all_provider_attempts_are_transient(message: &str) -> bool { /// Defense-in-depth filter for the Sentry `before_send` hook: the primary /// suppression lives at the call sites in `agent::harness::session:: /// runtime::run_single`, `channels::runtime::dispatch`, and -/// `channels::providers::web::run_chat_task`, all of which now skip +/// `web_chat::run_chat_task`, all of which now skip /// `report_error` when this variant is detected. This filter catches any /// future call site that re-emits the message without going through those /// funnels — e.g. a new wrapper that calls `tracing::error!` directly with diff --git a/src/core/socketio.rs b/src/core/socketio.rs index 84afc6fc84..9459c21ddf 100644 --- a/src/core/socketio.rs +++ b/src/core/socketio.rs @@ -505,7 +505,7 @@ pub fn attach_socketio() -> (socketioxide::layer::SocketIoLayer, SocketIo) { ); // Trigger the web channel's chat logic. - match crate::openhuman::channels::providers::web::start_chat( + match crate::openhuman::web_chat::start_chat( &client_id, &payload.thread_id, &payload.message, @@ -514,7 +514,7 @@ pub fn attach_socketio() -> (socketioxide::layer::SocketIoLayer, SocketIo) { payload.profile_id, payload.locale, payload.queue_mode, - crate::openhuman::channels::providers::web::ChatRequestMetadata::default(), + crate::openhuman::web_chat::ChatRequestMetadata::default(), ) .await { @@ -556,7 +556,7 @@ pub fn attach_socketio() -> (socketioxide::layer::SocketIoLayer, SocketIo) { client_id, payload.thread_id ); - let _ = crate::openhuman::channels::providers::web::cancel_chat_scoped( + let _ = crate::openhuman::web_chat::cancel_chat_scoped( &client_id, &payload.thread_id, payload.request_id.as_deref(), @@ -607,7 +607,7 @@ pub fn spawn_web_channel_bridge(io: SocketIo) { // 1. Web channel events → per-client rooms. let io_web = io.clone(); tokio::spawn(async move { - let mut rx = crate::openhuman::channels::providers::web::subscribe_web_channel_events(); + let mut rx = crate::openhuman::web_chat::subscribe_web_channel_events(); loop { let event = match rx.recv().await { Ok(event) => event, diff --git a/src/main.rs b/src/main.rs index 45324defe9..fbd3b41672 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,7 +119,7 @@ fn main() { // slipped past the call-site filters in // `agent::harness::session::runtime::run_single`, // `channels::runtime::dispatch`, and - // `channels::providers::web::run_chat_task`. The cap is a + // `web_chat::run_chat_task`. The cap is a // deterministic agent-state outcome surfaced to the user via // the chat-rendered "Error: …" message — Sentry is the wrong // surface for it (OPENHUMAN-TAURI-99 / -98). diff --git a/src/openhuman/agent/README.md b/src/openhuman/agent/README.md index eb36cba9ad..38bd21ae24 100644 --- a/src/openhuman/agent/README.md +++ b/src/openhuman/agent/README.md @@ -27,7 +27,7 @@ Multi-agent orchestration domain. Owns the LLM tool-calling loop, sub-agent disp ## Called by -- `src/openhuman/channels/runtime/dispatch.rs` and `channels/providers/web.rs` — drive chat turns from inbound channel messages. +- `src/openhuman/channels/runtime/dispatch.rs` and `web_chat/` — drive chat turns from inbound channel messages. - `src/openhuman/cron/scheduler.rs` — fire scheduled triggers through `triage::run_triage` + `apply_decision`. - `src/openhuman/webhooks/ops.rs` — webhook ingestion routes through triage. - `src/openhuman/composio/bus.rs` — Composio trigger envelopes go through `agent::triage`. diff --git a/src/openhuman/agent/progress_tracing/journal_projection.rs b/src/openhuman/agent/progress_tracing/journal_projection.rs index f15acb8812..5665df51c5 100644 --- a/src/openhuman/agent/progress_tracing/journal_projection.rs +++ b/src/openhuman/agent/progress_tracing/journal_projection.rs @@ -4,7 +4,7 @@ //! [`AgentObservation`] journal (the crate `AgentEvent` record) and folds it //! through the existing [`SpanCollector`], so trace spans no longer require the //! *live* in-run `AgentProgress` side-observer -//! (`channels/providers/web/progress_bridge.rs`). A UI/supervisor can attach +//! (`web_chat/progress_bridge.rs`). A UI/supervisor can attach //! after a run, read the journal, and rebuild identical spans. //! //! This is deliberately built on `SpanCollector` (not a re-derivation) so span diff --git a/src/openhuman/agent/task_dispatcher/executor.rs b/src/openhuman/agent/task_dispatcher/executor.rs index d54571bfca..466f5ab99d 100644 --- a/src/openhuman/agent/task_dispatcher/executor.rs +++ b/src/openhuman/agent/task_dispatcher/executor.rs @@ -195,13 +195,13 @@ pub(super) async fn run_autonomous( if let Some(thread_id) = session_thread_id.as_deref() { let (progress_tx, progress_rx) = tokio::sync::mpsc::channel(64); agent.set_on_progress(Some(progress_tx)); - crate::openhuman::channels::providers::web::spawn_progress_bridge( + crate::openhuman::web_chat::spawn_progress_bridge( progress_rx, "system".to_string(), thread_id.to_string(), run_id.to_string(), crate::openhuman::threads::turn_state::TurnStateStore::new(workspace_dir.clone()), - crate::openhuman::channels::providers::web::ChatRequestMetadata { + crate::openhuman::web_chat::ChatRequestMetadata { // Trace attribution: mark the run autonomous and carry the // resolved executor agent so Langfuse traces read // `agent.turn:` with channel.source=autonomous. @@ -251,7 +251,7 @@ pub(super) async fn run_autonomous( if let Some(thread_id) = session_thread_id.as_deref() { match &result { Ok(response) => { - crate::openhuman::channels::providers::web::presentation::deliver_response( + crate::openhuman::web_chat::presentation::deliver_response( "system", thread_id, run_id, @@ -265,7 +265,7 @@ pub(super) async fn run_autonomous( .await; } Err(err) => { - crate::openhuman::channels::providers::web::publish_web_channel_event( + crate::openhuman::web_chat::publish_web_channel_event( crate::core::socketio::WebChannelEvent { event: "chat_error".to_string(), client_id: "system".to_string(), diff --git a/src/openhuman/agent/task_dispatcher/registry.rs b/src/openhuman/agent/task_dispatcher/registry.rs index 375f369397..2d88c34fc6 100644 --- a/src/openhuman/agent/task_dispatcher/registry.rs +++ b/src/openhuman/agent/task_dispatcher/registry.rs @@ -106,17 +106,15 @@ fn cancel_taken_run(thread_id: &str, run: ActiveRun) { &run.run_id, Err("Cancelled by user".to_string()), ); - crate::openhuman::channels::providers::web::publish_web_channel_event( - crate::core::socketio::WebChannelEvent { - event: "chat_error".to_string(), - client_id: "system".to_string(), - thread_id: thread_id.to_string(), - request_id: run.run_id.clone(), - message: Some("Cancelled".to_string()), - error_type: Some("cancelled".to_string()), - ..Default::default() - }, - ); + crate::openhuman::web_chat::publish_web_channel_event(crate::core::socketio::WebChannelEvent { + event: "chat_error".to_string(), + client_id: "system".to_string(), + thread_id: thread_id.to_string(), + request_id: run.run_id.clone(), + message: Some("Cancelled".to_string()), + error_type: Some("cancelled".to_string()), + ..Default::default() + }); tracing::info!( thread_id = %thread_id, card_id = %run.card_id, diff --git a/src/openhuman/agent/triage/evaluator.rs b/src/openhuman/agent/triage/evaluator.rs index 351e95e89e..e7a6fbcba2 100644 --- a/src/openhuman/agent/triage/evaluator.rs +++ b/src/openhuman/agent/triage/evaluator.rs @@ -647,7 +647,7 @@ fn is_prompt_guard_rejection(message: &str) -> bool { /// /// The vocabulary matches the OpenHuman backend's error copy and common /// third-party provider phrasing. It does **not** mirror the -/// *semantics* of `channels/providers/web.rs` (a different code path); +/// *semantics* of `web_chat/` (a different code path); /// it is an independent, conservative allowlist evaluated inline so the /// triage evaluator carries no cross-domain import. /// diff --git a/src/openhuman/agent/turn_origin.rs b/src/openhuman/agent/turn_origin.rs index 49955a63ed..ef712b5837 100644 --- a/src/openhuman/agent/turn_origin.rs +++ b/src/openhuman/agent/turn_origin.rs @@ -4,7 +4,7 @@ //! consistent decisions across web, channel, subconscious, and cron entry //! points without relying on the *absence* of other task-locals as a signal. //! -//! Every entry point that drives the agent loop ([`crate::openhuman::channels::providers::web`], +//! Every entry point that drives the agent loop ([`crate::openhuman::web_chat`], //! [`crate::openhuman::channels::runtime::dispatch`], [`crate::openhuman::subconscious`], //! [`crate::openhuman::cron`], CLI) MUST scope a real [`AgentTurnOrigin`] //! around its `run_turn` invocation. Any path that fails to do so is treated diff --git a/src/openhuman/agent_orchestration/command_center/mod.rs b/src/openhuman/agent_orchestration/command_center/mod.rs index 13b937da23..d1e6aa0447 100644 --- a/src/openhuman/agent_orchestration/command_center/mod.rs +++ b/src/openhuman/agent_orchestration/command_center/mod.rs @@ -5,7 +5,7 @@ //! a normalized status model (needs-input / working / completed / failed / //! stopped) so users can see what is in flight, what is blocked on them, and //! what finished. Live run state already persists to the ledger via the spawn -//! tools + `channels::providers::web::progress_bridge`; this module only +//! tools + `web_chat::progress_bridge`; this module only //! projects and groups it for display. //! //! Control verbs (stop / retry / continue / follow-up) live in [`control`]: diff --git a/src/openhuman/agent_orchestration/run_ledger_finalize.rs b/src/openhuman/agent_orchestration/run_ledger_finalize.rs index be4a7cf7ae..4133a42114 100644 --- a/src/openhuman/agent_orchestration/run_ledger_finalize.rs +++ b/src/openhuman/agent_orchestration/run_ledger_finalize.rs @@ -12,7 +12,7 @@ //! — the *spawning turn's* progress channel. //! //! The run-ledger's terminal transition used to live **only** in the per-turn -//! [`progress_bridge`](crate::openhuman::channels::providers::web::progress_bridge), +//! [`progress_bridge`](crate::openhuman::web_chat::progress_bridge), //! which consumes that progress channel. That works for synchronous subagents //! (they finish inside the turn, sink still alive) but **leaks** for detached //! `spawn_async_subagent` runs: they outlive the parent turn, so when they diff --git a/src/openhuman/agentbox/invoker.rs b/src/openhuman/agentbox/invoker.rs index fca699ad2c..8c6983d001 100644 --- a/src/openhuman/agentbox/invoker.rs +++ b/src/openhuman/agentbox/invoker.rs @@ -10,11 +10,9 @@ use std::sync::Arc; use tokio::sync::broadcast::error::RecvError; use crate::core::socketio::WebChannelEvent; -use crate::openhuman::channels::providers::web::{ - start_chat, subscribe_web_channel_events, ChatRequestMetadata, -}; use crate::openhuman::memory::rpc_models::CreateConversationThreadRequest; use crate::openhuman::threads::ops::thread_create_new; +use crate::openhuman::web_chat::{start_chat, subscribe_web_channel_events, ChatRequestMetadata}; /// Outcome of inspecting one broadcast event against the request we're /// awaiting. Extracted as a pure function so the request-id filtering and diff --git a/src/openhuman/approval/README.md b/src/openhuman/approval/README.md index 18c0b48d73..416227fc2a 100644 --- a/src/openhuman/approval/README.md +++ b/src/openhuman/approval/README.md @@ -56,7 +56,7 @@ None. This module gates other domains' tools; it owns no tools of its own (no `t Published via `publish_global` (domain `approval`, defined in `src/core/event_bus/events.rs`): -- `DomainEvent::ApprovalRequested { request_id, tool_name, action_summary, args_redacted, session_id, thread_id, client_id }` — emitted when a call is parked. Bridged to the `approval_request` web-channel socket event by `ApprovalSurfaceSubscriber` (defined in `src/openhuman/channels/providers/web.rs`). +- `DomainEvent::ApprovalRequested { request_id, tool_name, action_summary, args_redacted, session_id, thread_id, client_id }` — emitted when a call is parked. Bridged to the `approval_request` web-channel socket event by `ApprovalSurfaceSubscriber` (defined in `src/openhuman/web_chat/`). - `DomainEvent::ApprovalDecided { request_id, tool_name, decision }` — emitted when a decision is applied. No `bus.rs` in this module — it only publishes; the subscriber lives in the `channels` web provider. @@ -80,7 +80,7 @@ SQLite DB at `{workspace_dir}/approval/approval.db`, table `pending_approvals` ( - `src/core/jsonrpc.rs` — installs the global gate (`ApprovalGate::init_global`) at startup; wires the approval RPCs. - `src/core/all.rs` — registers the controller schemas. - `src/openhuman/tinyagents/middleware.rs` (`ApprovalSecurityMiddleware`, a `wrap_tool` middleware on every turn path) — routes external-effect tool calls through the gate before `execute()` and records the terminal audit row. -- `src/openhuman/channels/providers/web.rs` — sets `APPROVAL_CHAT_CONTEXT`, hosts `ApprovalSurfaceSubscriber`, and routes typed yes/no replies to `approval_decide`. +- `src/openhuman/web_chat/` — sets `APPROVAL_CHAT_CONTEXT`, hosts `ApprovalSurfaceSubscriber`, and routes typed yes/no replies to `approval_decide`. - `src/openhuman/channels/proactive.rs`, `src/openhuman/agent/triage/escalation.rs`, `src/openhuman/tools/impl/system/install_tool.rs`, `src/openhuman/wallet/execution.rs` — interact with the gate / approval types. ## Notes / gotchas diff --git a/src/openhuman/approval/gate.rs b/src/openhuman/approval/gate.rs index 3b11a213b0..afde88c84b 100644 --- a/src/openhuman/approval/gate.rs +++ b/src/openhuman/approval/gate.rs @@ -71,7 +71,7 @@ const IN_CALL_APPROVAL_TTL: Duration = Duration::from_secs(120); /// Per-turn chat context for routing a parked approval's yes/no reply back to /// the originating thread. The web channel scopes this task-local around the -/// agent run (`channels::providers::web`); because the `run_turn` handler, the +/// agent run (`web_chat`); because the `run_turn` handler, the /// tool loop, and `intercept` all run inline (`.await`) within that spawned /// task, it propagates down to `intercept` with no signature plumbing. Absent /// for non-chat callers (CLI, sub-agents) — their approvals are simply not @@ -815,7 +815,7 @@ impl ApprovalGate { // Flow-origin surface bridge (flow-approval-surface, PR3): a flow run // has no chat thread/client to route the generic `ApprovalRequested` // through (both are `None` above, so the web-channel bridge silently - // drops it — see `channels::providers::web::event_bus`'s + // drops it — see `web_chat::event_bus`'s // `ApprovalSurfaceSubscriber`), which is exactly the silent-deadlock // bug this correlation fixes. Broadcast a dedicated // `flow_approval_request` socket event (no thread/client required, @@ -1340,7 +1340,7 @@ mod tests { /// A matching web-chat origin for the chat context fixture. Tests /// exercising the parking flow scope BOTH task-locals — production - /// callers in `channels/providers/web` do the same. + /// callers in `web_chat` do the same. fn web_origin() -> AgentTurnOrigin { AgentTurnOrigin::WebChat { thread_id: "t-test".into(), diff --git a/src/openhuman/artifacts/store.rs b/src/openhuman/artifacts/store.rs index 9d21de44b1..9255a871a9 100644 --- a/src/openhuman/artifacts/store.rs +++ b/src/openhuman/artifacts/store.rs @@ -616,7 +616,7 @@ pub async fn fail_artifact( } /// Read the active [`ApprovalChatContext`] task-local (set by -/// `channels::providers::web` around each chat turn) and return its +/// `web_chat` around each chat turn) and return its /// thread + client ids. Returns `(None, None)` for non-chat callers /// (CLI, cron, sub-agent runners) so artifact emit hooks degrade /// gracefully — the event is still published but the web subscriber diff --git a/src/openhuman/channels/bus.rs b/src/openhuman/channels/bus.rs index e82267d400..6ee6d355ba 100644 --- a/src/openhuman/channels/bus.rs +++ b/src/openhuman/channels/bus.rs @@ -87,10 +87,9 @@ impl EventHandler for ChannelInboundSubscriber { // here so the surface stays segregated end-to-end. let client_id = derive_inbound_client_id(channel, sender.as_deref()); - let mut event_rx = - crate::openhuman::channels::providers::web::subscribe_web_channel_events(); + let mut event_rx = crate::openhuman::web_chat::subscribe_web_channel_events(); - let request_id = match crate::openhuman::channels::providers::web::start_chat( + let request_id = match crate::openhuman::web_chat::start_chat( &client_id, &thread_id, message, @@ -99,7 +98,7 @@ impl EventHandler for ChannelInboundSubscriber { None, None, None, - crate::openhuman::channels::providers::web::ChatRequestMetadata { + crate::openhuman::web_chat::ChatRequestMetadata { // Tag inbound provider messages so traces classify as // run:channel_inbound instead of interactive chat. source: Some("channel_inbound".to_string()), diff --git a/src/openhuman/channels/host/adapters.rs b/src/openhuman/channels/host/adapters.rs index 8a017aad1c..8b0367ddc4 100644 --- a/src/openhuman/channels/host/adapters.rs +++ b/src/openhuman/channels/host/adapters.rs @@ -352,7 +352,7 @@ impl EventSink for OpenHumanEventSink { "{LOG_PREFIX} web event payload not a WebChannelEvent ({kind}): {e}" ) })?; - crate::openhuman::channels::providers::web::publish_web_channel_event(event); + crate::openhuman::web_chat::publish_web_channel_event(event); } "channel" => { use crate::core::event_bus::{publish_global, DomainEvent}; diff --git a/src/openhuman/channels/mod.rs b/src/openhuman/channels/mod.rs index e869a3876a..f28dc7d379 100644 --- a/src/openhuman/channels/mod.rs +++ b/src/openhuman/channels/mod.rs @@ -30,7 +30,6 @@ pub use providers::qq; pub use providers::signal; pub use providers::slack; pub use providers::telegram; -pub use providers::web; pub use providers::whatsapp; #[cfg(feature = "whatsapp-web")] pub use providers::whatsapp_web; diff --git a/src/openhuman/channels/proactive.rs b/src/openhuman/channels/proactive.rs index 9298fb9dea..fe9f762d42 100644 --- a/src/openhuman/channels/proactive.rs +++ b/src/openhuman/channels/proactive.rs @@ -21,8 +21,8 @@ use crate::core::event_bus::{DomainEvent, EventHandler}; use crate::core::socketio::WebChannelEvent; -use crate::openhuman::channels::providers::web::publish_web_channel_event; use crate::openhuman::channels::{Channel, ChannelSendExt, SendMessage}; +use crate::openhuman::web_chat::publish_web_channel_event; use async_trait::async_trait; use std::collections::HashMap; use std::sync::{Arc, RwLock}; diff --git a/src/openhuman/channels/providers/mod.rs b/src/openhuman/channels/providers/mod.rs index 37534b47b4..37f9bac96a 100644 --- a/src/openhuman/channels/providers/mod.rs +++ b/src/openhuman/channels/providers/mod.rs @@ -12,7 +12,6 @@ pub mod qq; pub mod signal; pub mod slack; pub mod telegram; -pub mod web; pub mod whatsapp; #[cfg(feature = "whatsapp-web")] pub mod whatsapp_web; diff --git a/src/openhuman/channels/providers/telegram/remote_control.rs b/src/openhuman/channels/providers/telegram/remote_control.rs index 93c35c232f..fe2d81c7bf 100644 --- a/src/openhuman/channels/providers/telegram/remote_control.rs +++ b/src/openhuman/channels/providers/telegram/remote_control.rs @@ -270,7 +270,7 @@ async fn build_new_session_response(ctx: &ChannelRuntimeContext, msg: &ChannelMe ); } - crate::openhuman::channels::providers::web::invalidate_thread_sessions(&thread_id).await; + crate::openhuman::web_chat::invalidate_thread_sessions(&thread_id).await; tracing::info!( "{LOG_PREFIX} new session thread_id={thread_id} reply_target={} sender_key={sender_key}", diff --git a/src/openhuman/channels/runtime/dispatch/processor.rs b/src/openhuman/channels/runtime/dispatch/processor.rs index d393109edb..973bf5d37b 100644 --- a/src/openhuman/channels/runtime/dispatch/processor.rs +++ b/src/openhuman/channels/runtime/dispatch/processor.rs @@ -89,7 +89,7 @@ pub(crate) fn channel_has_approval_surface(channel: &str) -> bool { /// Otherwise return `false` so the caller can dispatch the message as a /// fresh turn (which intentionally cancels any parked approval — the /// user is redirecting). Mirrors the web channel intercept at -/// `channels/providers/web.rs:493-525`. +/// `web_chat/`. /// /// [`ApprovalGate::decide`]: crate::openhuman::approval::ApprovalGate::decide async fn try_route_approval_reply(msg: &traits::ChannelMessage) -> bool { @@ -185,7 +185,7 @@ pub(crate) async fn process_channel_runtime_message( // history key, route it to `ApprovalGate::decide` and return — running // a fresh agent turn would cancel the parked tool call. Any other text // falls through to the normal dispatch (the user is redirecting). Mirrors - // the same intercept in `channels/providers/web.rs:493-525`. + // the same intercept in `web_chat/`. if channel_has_approval_surface(&msg.channel) && try_route_approval_reply(&msg).await { return; } diff --git a/src/openhuman/channels/runtime/startup.rs b/src/openhuman/channels/runtime/startup.rs index b3a13b5a11..59716ef3cf 100644 --- a/src/openhuman/channels/runtime/startup.rs +++ b/src/openhuman/channels/runtime/startup.rs @@ -166,20 +166,20 @@ pub async fn start_channels(mut config: Config) -> Result<()> { crate::openhuman::agent_meetings::bus::register_meeting_event_subscriber(); // Surface parked ApprovalGate requests as chat messages so the user can // answer yes/no in the thread (chat-native approval, issue #1339). - crate::openhuman::channels::providers::web::register_approval_surface_subscriber(); + crate::openhuman::web_chat::register_approval_surface_subscriber(); // Surface generated-artifact lifecycle events (ArtifactReady / // ArtifactFailed) as `artifact_ready` / `artifact_failed` web-channel // events so the frontend ArtifactCard can render in chat (#2779). - crate::openhuman::channels::providers::web::register_artifact_surface_subscriber(); + crate::openhuman::web_chat::register_artifact_surface_subscriber(); // Bridge emergency-stop halt/resume (AutomationHalted / AutomationResumed) // to the `automation_halt` web-channel socket event, broadcast to every // client via the "system" room, so the frontend kill-switch UI updates // globally (#4255). - crate::openhuman::channels::providers::web::register_automation_halt_subscriber(); + crate::openhuman::web_chat::register_automation_halt_subscriber(); // Surface external-egress disclosure events (ExternalTransferPending) as // `external_transfer_pending` web-channel events so the frontend can show a // per-action "what leaves, to where, why" card (privacy epic S2, #4436). - crate::openhuman::channels::providers::web::register_egress_surface_subscriber(); + crate::openhuman::web_chat::register_egress_surface_subscriber(); // Spawn the per-toolkit provider periodic sync scheduler. This is // a thin tokio task that ticks every minute and dispatches into // any provider whose `sync_interval_secs` has elapsed for an diff --git a/src/openhuman/cron/scheduler.rs b/src/openhuman/cron/scheduler.rs index 1c869beb21..115e28fdc5 100644 --- a/src/openhuman/cron/scheduler.rs +++ b/src/openhuman/cron/scheduler.rs @@ -76,7 +76,7 @@ fn agent_error_to_user_message(err: &AgentError) -> &'static str { // contract, for clean notification-drawer rendering) names // the two highest-signal remedies — credits and model // configuration. The richer three-remedy copy lives on the - // chat-surface side (`channels/providers/web_errors.rs`'s + // chat-surface side (`web_chat/web_errors.rs`'s // empty_response arm) where there's no drawer-width limit. "Empty model response. Out of credits (Settings \u{2192} Billing) or try another model in Connections \u{2192} API keys \u{2192} LLM." } @@ -764,15 +764,13 @@ fn permanent_halt_message(credits_exhausted: bool, budget_exhausted: bool) -> &' /// deep-link action even though no chat thread is active. fn publish_cron_user_error(kind: &str) { log::debug!("[cron] action=surface_user_error kind={kind}"); - crate::openhuman::channels::providers::web::publish_web_channel_event( - crate::core::socketio::WebChannelEvent { - event: "user_error".to_string(), - client_id: "system".to_string(), - error_type: Some(kind.to_string()), - error_source: Some("cron".to_string()), - ..Default::default() - }, - ); + crate::openhuman::web_chat::publish_web_channel_event(crate::core::socketio::WebChannelEvent { + event: "user_error".to_string(), + client_id: "system".to_string(), + error_type: Some(kind.to_string()), + error_source: Some("cron".to_string()), + ..Default::default() + }); } async fn process_due_jobs(config: &Config, security: &Arc, jobs: Vec) { diff --git a/src/openhuman/cron/scheduler_tests.rs b/src/openhuman/cron/scheduler_tests.rs index fc5b31df3b..f14212282f 100644 --- a/src/openhuman/cron/scheduler_tests.rs +++ b/src/openhuman/cron/scheduler_tests.rs @@ -1866,7 +1866,7 @@ fn next_user_error( #[test] fn publish_cron_user_error_broadcasts_metadata_only_for_each_kind() { - use crate::openhuman::channels::providers::web::subscribe_web_channel_events; + use crate::openhuman::web_chat::subscribe_web_channel_events; // Folded from two tests that both published `api_key_missing` to the // process-global bus and could false-pass off each other's broadcast under diff --git a/src/openhuman/flows/ops.rs b/src/openhuman/flows/ops.rs index 1ee18d0563..62a2968054 100644 --- a/src/openhuman/flows/ops.rs +++ b/src/openhuman/flows/ops.rs @@ -4018,13 +4018,13 @@ fn attach_flow_progress_bridge( source = %source, "[flows] progress bridge: attaching (streaming copilot/scout turn)" ); - crate::openhuman::channels::providers::web::spawn_progress_bridge( + crate::openhuman::web_chat::spawn_progress_bridge( progress_rx, "system".to_string(), target.thread_id.clone(), target.request_id.clone(), crate::openhuman::threads::turn_state::TurnStateStore::new(config.workspace_dir.clone()), - crate::openhuman::channels::providers::web::ChatRequestMetadata { + crate::openhuman::web_chat::ChatRequestMetadata { source: Some(source.to_string()), ..Default::default() }, @@ -4046,7 +4046,7 @@ async fn finalize_flow_stream( ) { match result { Ok(text) => { - crate::openhuman::channels::providers::web::presentation::deliver_response( + crate::openhuman::web_chat::presentation::deliver_response( "system", &target.thread_id, &target.request_id, @@ -4060,7 +4060,7 @@ async fn finalize_flow_stream( .await; } Err(err) => { - crate::openhuman::channels::providers::web::publish_web_channel_event( + crate::openhuman::web_chat::publish_web_channel_event( crate::core::socketio::WebChannelEvent { event: "chat_error".to_string(), client_id: "system".to_string(), diff --git a/src/openhuman/inference/provider/error_code.rs b/src/openhuman/inference/provider/error_code.rs index d2825ac37f..a69e838d52 100644 --- a/src/openhuman/inference/provider/error_code.rs +++ b/src/openhuman/inference/provider/error_code.rs @@ -13,7 +13,7 @@ //! runs their own provider key, no `errorCode` is present). The presence of an //! `errorCode` is therefore the single load-bearing signal for two decisions: //! -//! 1. **Classification** ([`super::super::super::channels::providers::web_errors::classify_inference_error`]): +//! 1. **Classification** ([`super::super::super::web_chat::web_errors::classify_inference_error`]): //! when an `errorCode` is present, branch on it FIRST and ignore the //! substring heuristics; when it is absent, fall back to the substring //! ladder (the BYO / direct-provider path, whose "check your API key" / diff --git a/src/openhuman/meet_agent/brain/llm.rs b/src/openhuman/meet_agent/brain/llm.rs index 5d54872bf8..47124b7db2 100644 --- a/src/openhuman/meet_agent/brain/llm.rs +++ b/src/openhuman/meet_agent/brain/llm.rs @@ -146,7 +146,7 @@ async fn get_or_build_agent_for_meet(request_id: &str) -> Result &str { - "channels::web::egress_surface" + "web_chat::egress_surface" } fn domains(&self) -> Option<&[&str]> { @@ -178,7 +178,7 @@ struct ArtifactSurfaceSubscriber; #[async_trait] impl EventHandler for ArtifactSurfaceSubscriber { fn name(&self) -> &str { - "channels::web::artifact_surface" + "web_chat::artifact_surface" } fn domains(&self) -> Option<&[&str]> { @@ -326,7 +326,7 @@ struct ApprovalSurfaceSubscriber; #[async_trait] impl EventHandler for ApprovalSurfaceSubscriber { fn name(&self) -> &str { - "channels::web::approval_surface" + "web_chat::approval_surface" } fn domains(&self) -> Option<&[&str]> { @@ -411,7 +411,7 @@ struct AutomationHaltSubscriber; #[async_trait] impl EventHandler for AutomationHaltSubscriber { fn name(&self) -> &str { - "channels::web::automation_halt" + "web_chat::automation_halt" } fn domains(&self) -> Option<&[&str]> { diff --git a/src/openhuman/channels/providers/web/mod.rs b/src/openhuman/web_chat/mod.rs similarity index 98% rename from src/openhuman/channels/providers/web/mod.rs rename to src/openhuman/web_chat/mod.rs index 4c979e8a78..2e8527413d 100644 --- a/src/openhuman/channels/providers/web/mod.rs +++ b/src/openhuman/web_chat/mod.rs @@ -9,7 +9,6 @@ mod schemas; mod session; mod types; -#[path = "../web_errors.rs"] mod web_errors; pub(crate) use web_errors::classify_inference_error; #[cfg(any(test, debug_assertions))] @@ -140,5 +139,5 @@ pub mod test_support { pub(crate) use types::SessionCacheFingerprint; #[cfg(test)] -#[path = "../web_tests.rs"] +#[path = "web_tests.rs"] mod tests; diff --git a/src/openhuman/channels/providers/web/ops.rs b/src/openhuman/web_chat/ops.rs similarity index 99% rename from src/openhuman/channels/providers/web/ops.rs rename to src/openhuman/web_chat/ops.rs index bc7a2a44c3..5a46c33067 100644 --- a/src/openhuman/channels/providers/web/ops.rs +++ b/src/openhuman/web_chat/ops.rs @@ -474,7 +474,7 @@ pub async fn start_chat( let prompt_decision = enforce_prompt_input( &message, PromptEnforcementContext { - source: "channels.providers.web.start_chat", + source: "web_chat.start_chat", request_id: Some(&request_id), user_id: Some(&client_id), session_id: Some(&thread_id), @@ -728,7 +728,7 @@ pub async fn start_chat( match result { Ok(chat_result) => { - crate::openhuman::channels::providers::web::presentation::deliver_response( + crate::openhuman::web_chat::presentation::deliver_response( &client_id_task, &thread_id_task, &request_id_task, @@ -931,7 +931,7 @@ async fn spawn_parallel_turn( match result { Some(Ok(chat_result)) => { - crate::openhuman::channels::providers::web::presentation::deliver_response( + crate::openhuman::web_chat::presentation::deliver_response( &client_id_task, &thread_id_task, &request_id_task, diff --git a/src/openhuman/channels/providers/web/presentation.rs b/src/openhuman/web_chat/presentation.rs similarity index 100% rename from src/openhuman/channels/providers/web/presentation.rs rename to src/openhuman/web_chat/presentation.rs diff --git a/src/openhuman/channels/providers/web/presentation_tests.rs b/src/openhuman/web_chat/presentation_tests.rs similarity index 100% rename from src/openhuman/channels/providers/web/presentation_tests.rs rename to src/openhuman/web_chat/presentation_tests.rs diff --git a/src/openhuman/channels/providers/web/progress_bridge.rs b/src/openhuman/web_chat/progress_bridge.rs similarity index 100% rename from src/openhuman/channels/providers/web/progress_bridge.rs rename to src/openhuman/web_chat/progress_bridge.rs diff --git a/src/openhuman/channels/providers/web/run_task.rs b/src/openhuman/web_chat/run_task.rs similarity index 100% rename from src/openhuman/channels/providers/web/run_task.rs rename to src/openhuman/web_chat/run_task.rs diff --git a/src/openhuman/channels/providers/web/schemas.rs b/src/openhuman/web_chat/schemas.rs similarity index 100% rename from src/openhuman/channels/providers/web/schemas.rs rename to src/openhuman/web_chat/schemas.rs diff --git a/src/openhuman/channels/providers/web/session.rs b/src/openhuman/web_chat/session.rs similarity index 100% rename from src/openhuman/channels/providers/web/session.rs rename to src/openhuman/web_chat/session.rs diff --git a/src/openhuman/channels/providers/web/types.rs b/src/openhuman/web_chat/types.rs similarity index 100% rename from src/openhuman/channels/providers/web/types.rs rename to src/openhuman/web_chat/types.rs diff --git a/src/openhuman/channels/providers/web_errors.rs b/src/openhuman/web_chat/web_errors.rs similarity index 100% rename from src/openhuman/channels/providers/web_errors.rs rename to src/openhuman/web_chat/web_errors.rs diff --git a/src/openhuman/channels/providers/web_tests.rs b/src/openhuman/web_chat/web_tests.rs similarity index 100% rename from src/openhuman/channels/providers/web_tests.rs rename to src/openhuman/web_chat/web_tests.rs diff --git a/tests/agent_harness_e2e.rs b/tests/agent_harness_e2e.rs index 8bbd69760a..0b1b82b866 100644 --- a/tests/agent_harness_e2e.rs +++ b/tests/agent_harness_e2e.rs @@ -1361,7 +1361,7 @@ encrypt = false /// same binary lose the bridge silently. This per-test helper avoids the issue by /// registering a fresh subscription on each test's own runtime. fn register_approval_bridge() -> Option { - openhuman_core::openhuman::channels::providers::web::fresh_approval_surface_subscription() + openhuman_core::openhuman::web_chat::fresh_approval_surface_subscription() } /// Pre-create a file in the action_dir so file_write sees it as an existing @@ -1473,7 +1473,7 @@ async fn approval_gate_approve_flow_inner() { .await; // Wait for the approval_request SSE event. - // Actual shape (src/openhuman/channels/providers/web/event_bus.rs:195-224): + // Actual shape (src/openhuman/web_chat/event_bus.rs:195-224): // { "event": "approval_request", "data": { "request_id": "...", "tool_name": "...", // "action_summary": "...", "args_redacted": {...} }, ... } let approval = wait_for_event(&mut events, "approval_request", Duration::from_secs(60)).await; diff --git a/tests/agentbox_e2e.rs b/tests/agentbox_e2e.rs index a44ca3b973..5521499e6a 100644 --- a/tests/agentbox_e2e.rs +++ b/tests/agentbox_e2e.rs @@ -14,7 +14,7 @@ //! //! The AgentBox `/run` invoker (`agentbox::invoker::CoreAgentInvoker`) drives //! the **live agent runtime** through the web-channel pipeline -//! (`channels::providers::web::start_chat`). End-to-end completion against a +//! (`web_chat::start_chat`). End-to-end completion against a //! freshly-bootstrapped tempdir workspace requires: //! //! 1. A logged-in user session on disk — `start_chat` and several upstream diff --git a/tests/composio_list_tools_stack_overflow_regression.rs b/tests/composio_list_tools_stack_overflow_regression.rs index 85c21916ec..565b08f583 100644 --- a/tests/composio_list_tools_stack_overflow_regression.rs +++ b/tests/composio_list_tools_stack_overflow_regression.rs @@ -22,7 +22,7 @@ //! ← subagent_runner::run_inner_loop / run_typed_mode / run_subagent //! ← SkillDelegationTool::execute (delegate_to_integrations_agent) //! ← Agent::execute_tool_call / execute_tools / turn -//! ← channels::providers::web::run_chat_task +//! ← web_chat::run_chat_task //! ``` //! //! The crash trigger is `composio_list_tools` reloading the config from @@ -50,7 +50,7 @@ //! ## What this test does //! //! Faithful reproduction in cargo-test is awkward: we can't easily -//! rebuild the upper chat-channel layers (`channels::providers::web:: +//! rebuild the upper chat-channel layers (`web_chat:: //! run_chat_task → Agent::turn → execute_tools → SkillDelegationTool`) //! without standing up an HTTP + Socket.IO stack. We drive the production //! path from `run_subagent` downward — i.e. everything below diff --git a/tests/raw_coverage/channels_bus_presentation_raw_coverage_e2e.rs b/tests/raw_coverage/channels_bus_presentation_raw_coverage_e2e.rs index 3e5cdb944c..5d1263e0f5 100644 --- a/tests/raw_coverage/channels_bus_presentation_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_bus_presentation_raw_coverage_e2e.rs @@ -8,8 +8,8 @@ use std::time::Duration; use openhuman_core::core::event_bus::{DomainEvent, EventHandler}; use openhuman_core::openhuman::agent_memory::memory_loader::MemoryCitation; use openhuman_core::openhuman::channels::bus::ChannelInboundSubscriber; -use openhuman_core::openhuman::channels::providers::web::presentation::test_support as presentation_test_support; -use openhuman_core::openhuman::channels::providers::web::{ +use openhuman_core::openhuman::web_chat::presentation::test_support as presentation_test_support; +use openhuman_core::openhuman::web_chat::{ subscribe_web_channel_events, test_support as web_test_support, }; use serde_json::json; diff --git a/tests/raw_coverage/channels_large_round25_raw_coverage_e2e.rs b/tests/raw_coverage/channels_large_round25_raw_coverage_e2e.rs index 11632f99bf..c0c11f97a0 100644 --- a/tests/raw_coverage/channels_large_round25_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_large_round25_raw_coverage_e2e.rs @@ -17,7 +17,7 @@ use openhuman_core::openhuman::channels::providers::mattermost::{ test_support as mattermost_support, MattermostChannel, }; use openhuman_core::openhuman::channels::providers::telegram::test_support as telegram_support; -use openhuman_core::openhuman::channels::providers::web::{self, test_support as web_support}; +use openhuman_core::openhuman::web_chat::{self as web, test_support as web_support}; use openhuman_core::openhuman::channels::test_support::{ build_channel_context_block_for_test, run_dispatch_harness, select_acknowledgment_reaction_for_test, DispatchHarnessOptions, TestMemoryEntry, diff --git a/tests/raw_coverage/channels_provider_deep_raw_coverage_e2e.rs b/tests/raw_coverage/channels_provider_deep_raw_coverage_e2e.rs index fb03729d8b..a69d01a252 100644 --- a/tests/raw_coverage/channels_provider_deep_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_provider_deep_raw_coverage_e2e.rs @@ -8,7 +8,7 @@ use axum::{ routing::post, Router, }; -use openhuman_core::openhuman::channels::providers::web::{ +use openhuman_core::openhuman::web_chat::{ cancel_chat, start_chat, subscribe_web_channel_events, ChatRequestMetadata, }; use openhuman_core::openhuman::channels::providers::yuanbao::{YuanbaoChannel, YuanbaoConfig}; @@ -310,7 +310,7 @@ async fn web_channel_validation_cancel_and_classifier_snapshots_are_publicly_exe assert!(blocked.is_err()); let rate_limited = - openhuman_core::openhuman::channels::web::test_support::classify_error_for_test( + openhuman_core::openhuman::web_chat::test_support::classify_error_for_test( r#"openrouter API error (429 Too Many Requests): {"error":{"message":"slow down","retry_after":1.2}}"#, ); assert_eq!(rate_limited.error_type, "rate_limited"); @@ -320,38 +320,38 @@ async fn web_channel_validation_cancel_and_classifier_snapshots_are_publicly_exe assert!(rate_limited.retryable); let business_429 = - openhuman_core::openhuman::channels::web::test_support::classify_error_for_test( + openhuman_core::openhuman::web_chat::test_support::classify_error_for_test( "zai API error (429): code 1311 no available package", ); assert_eq!(business_429.error_type, "rate_limited"); assert!(!business_429.retryable); let action_budget = - openhuman_core::openhuman::channels::web::test_support::classify_error_for_test( + openhuman_core::openhuman::web_chat::test_support::classify_error_for_test( "rate limit exceeded: action budget exhausted", ); assert_eq!(action_budget.error_type, "action_budget_exceeded"); assert_eq!(action_budget.source, "openhuman_budget"); assert_eq!(action_budget.provider, None); - let exhausted = openhuman_core::openhuman::channels::web::test_support::classify_error_for_test( + let exhausted = openhuman_core::openhuman::web_chat::test_support::classify_error_for_test( "All providers/models failed. Attempts: openai API error (503 Service Unavailable)", ); assert_eq!(exhausted.fallback_available, Some(false)); assert_eq!( - openhuman_core::openhuman::channels::web::test_support::retry_after_secs_for_test( + openhuman_core::openhuman::web_chat::test_support::retry_after_secs_for_test( r#"{"retry_after": 0.1}"# ), Some(1) ); - assert!(openhuman_core::openhuman::channels::web::test_support::extracted_provider_detail_for_test( + assert!(openhuman_core::openhuman::web_chat::test_support::extracted_provider_detail_for_test( r#"openai API error (404): {"error":{"message":"Project does not have access to model x"}}"# ) .expect("provider detail") .contains("model")); assert!( - openhuman_core::openhuman::channels::web::test_support::is_non_retryable_rate_limit_for_test( + openhuman_core::openhuman::web_chat::test_support::is_non_retryable_rate_limit_for_test( "plan does not include this model" ) ); diff --git a/tests/raw_coverage/channels_provider_leftovers_raw_coverage_e2e.rs b/tests/raw_coverage/channels_provider_leftovers_raw_coverage_e2e.rs index 89bebebda1..8dc6d1d95e 100644 --- a/tests/raw_coverage/channels_provider_leftovers_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_provider_leftovers_raw_coverage_e2e.rs @@ -15,7 +15,7 @@ use axum::{ Router, }; use openhuman_core::openhuman::channels::providers::telegram::TelegramChannel; -use openhuman_core::openhuman::channels::providers::web::{ +use openhuman_core::openhuman::web_chat::{ cancel_chat, start_chat, subscribe_web_channel_events, test_support as web_test_support, ChatRequestMetadata, }; diff --git a/tests/raw_coverage/channels_runtime_raw_coverage_e2e.rs b/tests/raw_coverage/channels_runtime_raw_coverage_e2e.rs index 7f97cf0d22..882f37bea7 100644 --- a/tests/raw_coverage/channels_runtime_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_runtime_raw_coverage_e2e.rs @@ -9,7 +9,7 @@ use axum::{ Router, }; use openhuman_core::core::event_bus::{DomainEvent, EventHandler}; -use openhuman_core::openhuman::channels::providers::web::{ +use openhuman_core::openhuman::web_chat::{ cancel_chat, start_chat, subscribe_web_channel_events, ChatRequestMetadata, }; use openhuman_core::openhuman::channels::providers::yuanbao::{YuanbaoChannel, YuanbaoConfig}; diff --git a/tests/raw_coverage/channels_web_startup_raw_coverage_e2e.rs b/tests/raw_coverage/channels_web_startup_raw_coverage_e2e.rs index a6d57ad4c9..2e0ff56143 100644 --- a/tests/raw_coverage/channels_web_startup_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_web_startup_raw_coverage_e2e.rs @@ -10,7 +10,7 @@ use openhuman_core::openhuman::channels::start_channels; use openhuman_core::openhuman::channels::test_support::{ lock_agent_handler, run_dispatch_harness, DispatchHarnessOptions, TestMemoryEntry, }; -use openhuman_core::openhuman::channels::web::{ +use openhuman_core::openhuman::web_chat::{ all_web_channel_controller_schemas, all_web_channel_registered_controllers, channel_web_cancel, channel_web_chat, schemas, start_chat, subscribe_web_channel_events, test_support as web_test_support, ChatRequestMetadata, diff --git a/tests/raw_coverage/channels_web_telegram_raw_coverage_e2e.rs b/tests/raw_coverage/channels_web_telegram_raw_coverage_e2e.rs index 7784797cce..ef1675d42a 100644 --- a/tests/raw_coverage/channels_web_telegram_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_web_telegram_raw_coverage_e2e.rs @@ -16,7 +16,7 @@ use axum::{ }; use openhuman_core::core::event_bus::{init_global, publish_global, DomainEvent}; use openhuman_core::openhuman::channels::providers::telegram::TelegramChannel; -use openhuman_core::openhuman::channels::providers::web::{ +use openhuman_core::openhuman::web_chat::{ cancel_chat, register_approval_surface_subscriber, start_chat, subscribe_web_channel_events, test_support as web_test_support, ChatRequestMetadata, }; diff --git a/tests/raw_coverage/channels_web_yuanbao_round22_raw_coverage_e2e.rs b/tests/raw_coverage/channels_web_yuanbao_round22_raw_coverage_e2e.rs index 687005c5d9..11872a3f0c 100644 --- a/tests/raw_coverage/channels_web_yuanbao_round22_raw_coverage_e2e.rs +++ b/tests/raw_coverage/channels_web_yuanbao_round22_raw_coverage_e2e.rs @@ -13,7 +13,7 @@ use axum::{ Router, }; use openhuman_core::openhuman::channels::providers::telegram::TelegramChannel; -use openhuman_core::openhuman::channels::providers::web::{ +use openhuman_core::openhuman::web_chat::{ cancel_chat, start_chat, subscribe_web_channel_events, test_support as web_test_support, ChatRequestMetadata, }; diff --git a/tests/raw_coverage/tools_approval_channels_raw_coverage_e2e.rs b/tests/raw_coverage/tools_approval_channels_raw_coverage_e2e.rs index f7ed574df5..7402dc39f1 100644 --- a/tests/raw_coverage/tools_approval_channels_raw_coverage_e2e.rs +++ b/tests/raw_coverage/tools_approval_channels_raw_coverage_e2e.rs @@ -2067,8 +2067,8 @@ async fn channel_provider_public_paths_cover_pre_network_errors_and_utilities() #[tokio::test] async fn web_channel_public_paths_cover_event_delivery_and_validation_errors() { - let mut rx = openhuman_core::openhuman::channels::web::subscribe_web_channel_events(); - openhuman_core::openhuman::channels::web::publish_web_channel_event(WebChannelEvent { + let mut rx = openhuman_core::openhuman::web_chat::subscribe_web_channel_events(); + openhuman_core::openhuman::web_chat::publish_web_channel_event(WebChannelEvent { event: "coverage_event".to_string(), client_id: "client-1".to_string(), thread_id: "thread-1".to_string(), @@ -2086,7 +2086,7 @@ async fn web_channel_public_paths_cover_event_delivery_and_validation_errors() { assert_eq!(event.message.as_deref(), Some("hello web channel")); assert_eq!( - openhuman_core::openhuman::channels::web::start_chat( + openhuman_core::openhuman::web_chat::start_chat( "", "thread-1", "hello", @@ -2095,14 +2095,14 @@ async fn web_channel_public_paths_cover_event_delivery_and_validation_errors() { None, None, None, - openhuman_core::openhuman::channels::web::ChatRequestMetadata::default(), + openhuman_core::openhuman::web_chat::ChatRequestMetadata::default(), ) .await .expect_err("blank client_id"), "client_id is required" ); assert_eq!( - openhuman_core::openhuman::channels::web::start_chat( + openhuman_core::openhuman::web_chat::start_chat( "client-1", "", "hello", @@ -2111,14 +2111,14 @@ async fn web_channel_public_paths_cover_event_delivery_and_validation_errors() { None, None, None, - openhuman_core::openhuman::channels::web::ChatRequestMetadata::default(), + openhuman_core::openhuman::web_chat::ChatRequestMetadata::default(), ) .await .expect_err("blank thread_id"), "thread_id is required" ); assert_eq!( - openhuman_core::openhuman::channels::web::start_chat( + openhuman_core::openhuman::web_chat::start_chat( "client-1", "thread-1", " ", @@ -2127,7 +2127,7 @@ async fn web_channel_public_paths_cover_event_delivery_and_validation_errors() { None, None, None, - openhuman_core::openhuman::channels::web::ChatRequestMetadata::default(), + openhuman_core::openhuman::web_chat::ChatRequestMetadata::default(), ) .await .expect_err("blank message"), @@ -2135,26 +2135,26 @@ async fn web_channel_public_paths_cover_event_delivery_and_validation_errors() { ); assert_eq!( - openhuman_core::openhuman::channels::web::cancel_chat("", "thread-1") + openhuman_core::openhuman::web_chat::cancel_chat("", "thread-1") .await .expect_err("blank cancel client_id"), "client_id is required" ); assert_eq!( - openhuman_core::openhuman::channels::web::cancel_chat("client-1", "") + openhuman_core::openhuman::web_chat::cancel_chat("client-1", "") .await .expect_err("blank cancel thread_id"), "thread_id is required" ); assert!( - openhuman_core::openhuman::channels::web::cancel_chat("client-1", "thread-1") + openhuman_core::openhuman::web_chat::cancel_chat("client-1", "thread-1") .await .expect("cancel with no in-flight request") .is_none() ); - openhuman_core::openhuman::channels::web::invalidate_thread_sessions("thread-1").await; + openhuman_core::openhuman::web_chat::invalidate_thread_sessions("thread-1").await; assert!( - openhuman_core::openhuman::channels::web::in_flight_entries_for_test() + openhuman_core::openhuman::web_chat::in_flight_entries_for_test() .await .is_empty() ); @@ -2181,7 +2181,7 @@ async fn proactive_subscriber_routes_web_and_active_external_channel_without_net } } - let mut rx = openhuman_core::openhuman::channels::web::subscribe_web_channel_events(); + let mut rx = openhuman_core::openhuman::web_chat::subscribe_web_channel_events(); let capture = Arc::new(CapturingChannel::default()); let mut channels: HashMap> = HashMap::new(); channels.insert("capture".into(), capture.clone()); diff --git a/tests/raw_coverage/tools_network_channels_raw_coverage_e2e.rs b/tests/raw_coverage/tools_network_channels_raw_coverage_e2e.rs index 97cb277dd2..5a9b254611 100644 --- a/tests/raw_coverage/tools_network_channels_raw_coverage_e2e.rs +++ b/tests/raw_coverage/tools_network_channels_raw_coverage_e2e.rs @@ -18,7 +18,7 @@ use tempfile::{tempdir, TempDir}; use tokio::time::timeout; use openhuman_core::core::socketio::WebChannelEvent; -use openhuman_core::openhuman::channels::providers::web::{ +use openhuman_core::openhuman::web_chat::{ all_web_channel_controller_schemas, all_web_channel_registered_controllers, cancel_chat, channel_web_cancel, publish_web_channel_event, schemas as web_channel_schema, start_chat, subscribe_web_channel_events, ChatRequestMetadata, diff --git a/tests/web_cancel_request_scoping.rs b/tests/web_cancel_request_scoping.rs index 4b79df0fab..c90f78c228 100644 --- a/tests/web_cancel_request_scoping.rs +++ b/tests/web_cancel_request_scoping.rs @@ -16,7 +16,7 @@ //! These assertions pin that decision. `cancel_should_target` is a pure //! predicate, so this is a fast, deterministic unit test with no runtime setup. -use openhuman_core::openhuman::channels::web::cancel_should_target; +use openhuman_core::openhuman::web_chat::cancel_should_target; #[test] fn unscoped_cancel_always_targets_the_in_flight_turn() {