Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 117 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ path = "src/bin/test_mcp_stub.rs"
[[bin]]
name = "openhuman-fleet"
path = "src/bin/fleet.rs"
# The fleet supervisor embeds the axum control-plane server, so it only builds
# with the HTTP transport compiled in (#5048). Under `--no-default-features`
# (no `http-server`) this target is skipped rather than failing to link.
required-features = ["http-server"]

# Embedded-RSS benchmark harness (#5046). Gated behind the default-OFF
# `rss-bench` feature so no benchmark code enters the shipped build. Build with
Expand Down Expand Up @@ -118,7 +122,15 @@ serde_yaml = "0.9"
# stripper (`fast_html_to_text` in
# providers/gmail/post_process.rs) and prefer the email's
# `text/plain` MIME part when available.)
reqwest = { version = "0.12", default-features = false, features = ["json", "blocking", "rustls-tls", "native-tls", "stream", "http2", "multipart", "socks"] }
# TLS: rustls only in the base declaration, so Linux/macOS — including the
# headless embedding target (#5046) — link a single TLS stack + cert set
# (Mozilla webpki-roots). Windows re-adds `native-tls` via the
# `[target.'cfg(windows)'.dependencies]` block below (Cargo unions features
# per target), because `src/openhuman/tls/mod.rs` deliberately routes the
# Windows client through the SChannel/OS cert store for corporate MITM +
# AV root CAs. So the two TLS backends coexist only on Windows, never on
# the RAM-sensitive platforms.
reqwest = { version = "0.12", default-features = false, features = ["json", "blocking", "rustls-tls", "stream", "http2", "multipart", "socks"] }
# Already in-tree via reqwest/hyper; named directly so `IntegrationClient::get_bytes`
# can return `bytes::Bytes` without a copy.
bytes = "1"
Expand Down Expand Up @@ -211,16 +223,22 @@ sysinfo = { version = "0.33", default-features = false, features = ["system"] }
keyring = { version = "3", features = ["apple-native", "windows-native", "linux-native"] }
clap = { version = "4.5", features = ["derive"] }
lettre = { version = "0.11.22", default-features = false, features = ["builder", "smtp-transport", "rustls-tls"], optional = true }
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio", "query", "ws", "macros"] }
tower = { version = "0.5", default-features = false }
sentry = { version = "0.47.0", default-features = false, features = ["backtrace", "contexts", "panic", "tracing", "debug-images", "reqwest", "rustls"] }
# HTTP + Socket.IO server transport — the `/rpc` JSON-RPC endpoint, `/v1`
# OpenAI-compat router, agentbox/http_host sub-servers, and the Socket.IO
# live-event bridge. Exclusive to the default-ON `http-server` feature (#5048):
# a slim/embedded build without it (`--no-default-features`) sheds `axum` +
# `socketioxide` and never binds a listener — the core still runs background
# services and answers over the CLI/native dispatch surface. See the
# `http-server` feature note below and `src/core/http_server_status.rs`.
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio", "query", "ws", "macros"], optional = true }
sentry = { version = "0.47.0", default-features = false, optional = true, features = ["backtrace", "contexts", "panic", "tracing", "debug-images", "reqwest", "rustls"] }
tokio-stream = { version = "0.1.18", features = ["full"] }
url = "2"
socketioxide = { version = "0.15", features = ["extensions"] }
whisper-rs = "0.16"
socketioxide = { version = "0.15", features = ["extensions"], optional = true }
whisper-rs = { version = "0.16", optional = true }
image = { version = "0.25", default-features = false, features = ["png", "jpeg"] }
tempfile = "3"
cpal = "0.15"
cpal = { version = "0.15", optional = true }
Comment thread
coderabbitai[bot] marked this conversation as resolved.
hound = { version = "3.5", optional = true }
enigo = "0.3"
arboard = "3"
Expand Down Expand Up @@ -249,11 +267,11 @@ coins-bip39 = "0.8"
curve25519-dalek = { version = "4", default-features = false, features = ["alloc"], optional = true }

fantoccini = { version = "0.22.0", optional = true, default-features = false, features = ["rustls-tls"] }
pdf-extract = "0.10"
pdf-extract = { version = "0.10", optional = true }
# The WhatsApp Web provider (and its `whatsapp-rust` / `wacore` / `serde-big-array`
# stack) now lives in the tinychannels crate; the `whatsapp-web` feature forwards
# to `tinychannels/whatsapp-web`.
ppt-rs = "0.2.14"
ppt-rs = { version = "0.2.14", optional = true }
# Terminal chat UI (`openhuman tui` / `chat`). Exclusive to the default-ON
# `tui` feature (see `[features]` below): a slim / headless build without `tui`
# drops both `ratatui` and `crossterm`. Kept in lockstep — ratatui 0.30
Expand All @@ -271,13 +289,19 @@ unicode-width = { version = "0.2", optional = true }
# `ppt-rs` presentation engine: synthesise bytes in-process, hand them to
# the byte-agnostic artifact pipeline. `default-features` keeps the crate's
# image support (unused here, but avoids a bespoke feature list drifting).
docx-rs = "0.4.20"
docx-rs = { version = "0.4.20", optional = true }

[target.'cfg(windows)'.dependencies]
# Windows: tokio-tungstenite uses native-tls (schannel) so wss://
# connections honor the Windows cert store, including corporate CAs
# installed by AV / TLS-inspection proxies. See run-dev-win.sh notes.
tokio-tungstenite = { version = "0.24", default-features = false, features = ["connect", "handshake", "native-tls"] }
# Windows re-adds reqwest's native-tls backend (dropped from the base decl in
# `[dependencies]`) so `tls::tls_client_builder()` can call `.use_native_tls()`
# and reach the SChannel/OS cert store — same rationale as tokio-tungstenite
# above. Cargo unions these features with the base rustls decl, so on Windows
# reqwest carries both backends; Linux/macOS keep rustls only.
reqwest = { version = "0.12", default-features = false, features = ["native-tls"] }
# AppContainer / process-jail backend in `openhuman::cwd_jail`.
# Feature list mirrors the Win32 surface used by cwd_jail/windows.rs:
# AppContainer profile APIs, ACL editing, STARTUPINFOEXW process spawn,
Expand Down Expand Up @@ -305,7 +329,7 @@ uiautomation = { version = "0.25", optional = true }
tokio-tungstenite = { version = "0.24", default-features = false, features = ["connect", "handshake", "rustls-tls-webpki-roots"] }

[target.'cfg(target_os = "macos")'.dependencies]
whisper-rs = { version = "0.16", features = ["metal"] }
whisper-rs = { version = "0.16", features = ["metal"], optional = true }
# Contacts framework bindings for address book seeding.
objc2 = "0.6"
objc2-foundation = { version = "0.3", features = ["NSArray", "NSError", "NSObject", "NSString", "NSPredicate"] }
Expand All @@ -324,6 +348,18 @@ rppal = { version = "0.22", optional = true }
# crates we never use (and that bloat the dev Cargo.lock noticeably).
# TestTransport only needs the `test` feature.
sentry = { version = "0.47.0", default-features = false, features = ["test"] }
# axum is optional in `[dependencies]` (exclusive to the default-ON
# `http-server` feature, #5048), but ~17 `#[cfg(test)]` modules stand up
# in-process axum mock servers / call `build_core_http_router` regardless of
# the feature set under test. Declaring a plain (non-optional) dev-dep keeps
# those tests compiling in ALL test builds without per-file `#[cfg]` — mirrors
# the `sentry` dual-declaration above. The prod fns those tests exercise are
# still gated, so the *tests* naming them carry `#[cfg(feature = "http-server")]`.
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio", "query", "ws", "macros"] }
# `tower` was a plain runtime dep only for its re-exports in the axum server
# path; with `http-server` optional it is test-only (tower::ServiceExt::oneshot
# drives the mock routers), so it lives here as a dev-dep now.
tower = { version = "0.5", default-features = false }
# Mock HTTP server for provider E2E tests (inference_provider_e2e).
wiremock = "0.6"
# Used in json_rpc_e2e to backdate mtime on stale lock files.
Expand All @@ -338,23 +374,66 @@ tokio = { version = "1", features = ["test-util"] }
proptest = "1"

[features]
default = ["tokenjuice-treesitter", "voice", "web3", "media", "meet", "skills", "flows", "mcp", "desktop-automation", "tui"]
default = ["tokenjuice-treesitter", "inference", "voice", "web3", "media", "documents", "meet", "skills", "flows", "mcp", "crash-reporting", "http-server", "desktop-automation", "tui"]
# HTTP + Socket.IO server transport (#5048): the `/rpc` JSON-RPC endpoint and
# its auth middleware/CORS layer (`core::jsonrpc`, `core::auth`), the `/v1`
# OpenAI-compatible router (`inference::http`), the ad-hoc static-dir file
# server (`openhuman::http_host`), the AgentBox `/run` HTTP surface
# (`agentbox::http`), the WebSocket dictation stream
# (`inference::voice::streaming`), the `openhuman text-input run` dev server,
# the MCP Streamable-HTTP transport (`mcp_server::http`, additionally gated by
# `mcp`), and the Socket.IO live-event bridge (`core::socketio`). Default-ON —
# the desktop shell REQUIRES it (see the `HTTP_SERVER_COMPILED_IN` compile
# assert in `app/src-tauri/src/lib.rs`). Slim / headless-embedding builds opt
# out via `--no-default-features --features "<explicit list without
# http-server>"`, which drops the exclusive `axum` + `socketioxide` deps; the
# core then runs background services without binding a listener (`serve()`
# returns early) and is driven over the CLI / native dispatch surface instead.
#
# TYPE CARVE-OUT (see AGENTS.md): `core::socketio`'s inert event payload types
# (`WebChannelEvent`, `TurnUsagePayload`, `SubagentUsagePayload`,
# `SubagentProgressDetail`) stay compiled in BOTH builds — ~10 always-on
# domains construct them — so `pub mod socketio;` is UNGATED and only the
# socketioxide/axum-touching bodies are gated. Likewise `inference::http::types`
# and `EXTERNAL_OPENAI_COMPAT_PROVIDER` stay compiled for `core::auth`.
http-server = ["dep:axum", "dep:socketioxide"]
# AST-aware code compression (tree-sitter Rust/TS/Python grammars; C build).
# On by default; disable to fall back to the brace-depth heuristic.
tokenjuice-treesitter = [
"tinyjuice/tinyjuice-treesitter",
]
# In-process inference engine: the whisper.cpp STT engine
# (`inference::local::service::whisper_engine`) plus the `cpal` audio-device
# probe shared by voice capture and the accessibility microphone-permission
# check. Default-ON. Slim / headless builds opt out via
# `--no-default-features --features "<explicit list without inference>"`, which
# drops the exclusive `whisper-rs` (including the macOS `metal` variant and the
# `whisper-rs-sys` patch, which go inert once whisper-rs leaves the graph) and
# `cpal` dependencies. When off, the whisper facade resolves to
# `whisper_engine::stub` — in-process STT returns a disabled error, while the
# local-AI service still reaches Ollama / LM Studio over HTTP — and the
# microphone probe reports `Unknown`. `voice` requires this gate, so building
# `voice` always pulls `inference` in transitively.
inference = ["dep:whisper-rs", "dep:cpal"]
# Office-document tools: the `generate_presentation` (ppt-rs) and
# `generate_document` (docx-rs) agent tools, plus `pdf-extract` for PDF text
# extraction during multimodal file ingest. Default-ON. Slim / headless builds
# opt out via `--no-default-features --features "<explicit list without
# documents>"`, which drops all three crates. Leaf gate (no stub facade): when
# off, the two tools are absent from the tool list rather than degraded to an
# error, and PDF ingest degrades the file to a reference instead of extracted
# text (`agent::multimodal::extract_pdf_text`).
documents = ["dep:pdf-extract", "dep:ppt-rs", "dep:docx-rs"]
# Voice + audio_toolkit domains: STT/TTS providers, the standalone dictation
# server, always-on listening, and podcast audio generation/email delivery.
# Default-ON — the desktop app always ships with voice. Slim / headless builds
# opt out via `--no-default-features --features "<explicit list without voice>"`,
# which also drops the exclusive `hound` (WAV I/O) + `lettre` (podcast email)
# dependencies. Composes with the runtime `DomainSet::voice` flag (#4796): the
# feature narrows the compile-time surface, `DomainSet` gates it at runtime.
# NOTE: this gate does NOT drop whisper-rs / llama / cpal — those live in the
# inference domain (shared with accessibility for cpal) and await a separate
# `inference` gate.
voice = ["dep:hound", "dep:lettre"]
# Requires `inference` (the whisper engine + the cpal capture stack), so
# enabling `voice` turns `inference` on transitively.
voice = ["inference", "dep:hound", "dep:lettre"]
# Web3 domains: openhuman::wallet + openhuman::web3 + openhuman::x402 — the
# crypto wallet (multi-chain sign/broadcast), the high-level swap/bridge/dapp
# surface, and the x402 machine-payment protocol. Default-ON — the desktop app
Expand Down Expand Up @@ -449,6 +528,28 @@ skills = []
# `sanitize::sanitize_for_llm`). The gate follows the real dependency graph,
# not the directory name.
mcp = []
# Sentry crash/error reporting: the `sentry::init` guard + secret-scrubbing
# `before_send` hook (src/main.rs), the sentry-tracing bridge layer
# (core::logging), the ~24 `before_send` Event classifiers + the two
# `report_*_message` capture paths (core::observability), the session-boundary
# scope binding (credentials::sentry_scope), and the `openhuman sentry-test`
# CLI probe. Default-ON — the desktop app always ships with crash reporting.
# Slim / headless builds opt out via `--no-default-features --features
# "<explicit list without crash-reporting>"`, which drops the exclusive
# `sentry` dependency (and its transitive backtrace/contexts/panic/tracing/
# debug-images/reqwest/rustls stack) from the non-test build.
#
# TYPE CARVE-OUT (see AGENTS.md "Compile-time domain gates"): the sentry-free
# string classifiers (`is_transient_message_failure`,
# `is_insufficient_credits_message`, `contains_transient_transport_phrase`, …)
# and the `report_*_message` / `sentry_scope::{bind,clear}` signatures stay
# compiled in BOTH builds — only the `sentry::`-touching bodies are gated — so
# NO stub file is needed and always-on callers need no per-call `#[cfg]`. When
# off, `report_error_message`/`report_warning_message` still emit their
# `tracing::{error,warn}!` diagnostic, `sentry_tracing_layer()` degrades to a
# no-op `Identity` layer, and `openhuman sentry-test` returns a "built without
# the crash-reporting feature" error.
crash-reporting = ["dep:sentry"]

# Desktop-automation cluster (#5049): the five modules that read/drive the local
# desktop UI — `openhuman::accessibility` (macOS AX / Windows UIA FFI middleware),
Expand Down Expand Up @@ -484,7 +585,6 @@ desktop-automation = ["dep:uiautomation"]
# build-fact "tui feature disabled at compile time" error from the untouched
# `"tui" | "chat"` CLI arm (mirrors the `mcp` stub pattern).
tui = ["dep:ratatui", "dep:crossterm", "dep:unicode-width"]

sandbox-landlock = ["dep:landlock"]
sandbox-bubblewrap = []
peripheral-rpi = ["dep:rppal"]
Expand Down
7 changes: 7 additions & 0 deletions app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,19 @@ cef = { version = "=146.4.1", default-features = false }
openhuman_core = { path = "../..", package = "openhuman", default-features = false, features = [
"media",
"tokenjuice-treesitter",
"inference",
"voice",
"web3",
"documents",
"flows",
"meet",
"skills",
"mcp",
"crash-reporting",
# The desktop shell reaches the in-process core only over
# http://127.0.0.1:<port>/rpc, so it REQUIRES the HTTP + Socket.IO transport
# (#5048). Enforced by the HTTP_SERVER_COMPILED_IN compile assert in lib.rs.
"http-server",
"desktop-automation",
] }
tinyjuice = { version = "0.2.1", default-features = false }
Expand Down
16 changes: 16 additions & 0 deletions app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ const _: () = assert!(
Add \"voice\" to the openhuman_core `features` list in app/src-tauri/Cargo.toml."
);

// The shell talks to the in-process core only over http://127.0.0.1:<port>/rpc,
// so the core MUST embed the HTTP + Socket.IO transport (#5048). Same failure
// class as #4901: with `http-server` dropped the core never binds a listener
// and every RPC is unreachable — silent and runtime-only. The marker lives in
// the core's always-compiled facade (`core::http_server_status`) precisely so
// this assert can observe the core's feature state (a dependent's own
// `#[cfg(feature = ...)]` would test THIS crate's features, not the core's).
const _: () = assert!(
openhuman_core::core::http_server_status::HTTP_SERVER_COMPILED_IN,
"openhuman_core must be built with the `http-server` feature: the desktop app reaches \
the core only over http://127.0.0.1:<port>/rpc, and without it the core binds no \
listener so every RPC is unreachable (#5048). \
Add \"http-server\" to the openhuman_core `features` list in app/src-tauri/Cargo.toml."
);

mod app_update;
// Artifact export commands (#2779, #3162) — both cross-platform
// (macOS/Windows/Linux): native Save-As dialog (rfd) + Downloads copy.
Expand Down Expand Up @@ -2387,6 +2402,7 @@ pub fn run() {
let custom_runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(openhuman_core::core::runtime::AGENT_WORKER_STACK_BYTES)
.max_blocking_threads(openhuman_core::core::runtime::MAX_BLOCKING_THREADS)
.build()
.expect("build custom tokio runtime for tauri async surface");
let handle = custom_runtime.handle().clone();
Expand Down
2 changes: 2 additions & 0 deletions src/core/agent_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ fn run_dump_all(args: &[String]) -> Result<()> {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(crate::core::runtime::AGENT_WORKER_STACK_BYTES)
.max_blocking_threads(crate::core::runtime::MAX_BLOCKING_THREADS)
.build()?;
log::debug!("[agent-cli] run_dump_all: calling dump_all_agent_prompts");
let dumps = rt.block_on(async {
Expand Down Expand Up @@ -255,6 +256,7 @@ fn run_dump_prompt(args: &[String]) -> Result<()> {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(crate::core::runtime::AGENT_WORKER_STACK_BYTES)
.max_blocking_threads(crate::core::runtime::MAX_BLOCKING_THREADS)
.build()?;
log::debug!("[agent-cli] run_dump_prompt: calling dump_agent_prompt");
let dumped = rt.block_on(async { dump_agent_prompt(options).await })?;
Expand Down
5 changes: 4 additions & 1 deletion src/core/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,10 @@ fn build_registered_controllers() -> Vec<GroupedController> {
DomainGroup::Platform,
crate::openhuman::heartbeat::all_heartbeat_registered_controllers(),
);
// Ad-hoc static directory HTTP hosting for local file sharing / previews
// Ad-hoc static directory HTTP hosting for local file sharing / previews.
// Gated with the `http-server` feature (#5048): the domain is an axum server,
// so a slim build has no `http_host.*` controllers to register.
#[cfg(feature = "http-server")]
push(
&mut controllers,
DomainGroup::Platform,
Expand Down
Loading
Loading