fix(core,cef): run core in-process and stop orphaning CEF helpers on Cmd+Q#1061
Conversation
Embed openhuman_core as a path dep in the Tauri shell and run its HTTP/JSON-RPC server as a tokio task inside the GUI process. Ties the core's lifetime to the Tauri host so Cmd+Q no longer orphans an `openhuman-core` child or its CEF helpers. - Drop CoreRunMode (InProcess/ChildProcess), all child-process spawn, SIGTERM/SIGKILL, and core_bin resolution from core_process.rs. - Delete core_update.rs (auto-update now via tauri-plugin-updater). - Stub check_core_update / apply_core_update IPC commands so the frontend keeps working without errors. - run_core_from_args dispatches into the linked lib instead of shelling out to a separate binary. - Remove externalBin from tauri.conf.json and the flate2/tar/zip/semver deps that only core_update used. The existing port-7788 probe in ensure_running still attaches to a manual `openhuman-core run` harness when one is already listening.
After moving the core in-process the visible failure mode of an unclean Cmd+Q is leftover "OpenHuman Helper" CEF processes. The vendored tauri-runtime-cef already calls cef::shutdown() after our RunEvent::Exit returns, but two gaps remained: 1. RunEvent::ExitRequested closes child webviews synchronously but the actual CEF teardown is async — a webview that was mid-load could race cef::shutdown() and leave its renderer orphaned. Add a 50ms sleep after our teardown so queued close messages reach CEF before the runtime drains the message loop. 2. Anything that survives cef::shutdown() (CEF bug or mid-spawn helper) gets re-parented to launchd / init when the GUI exits. Add a `pkill -TERM -P <pid>` sweep after the Tauri event loop returns so leftover direct children are notified before our process exits. The status code is logged at warn level when it actually killed something, so a real leak is visible in logs. No-op on Windows where job objects already terminate the CEF helper tree when the parent exits.
📝 WalkthroughWalkthroughThis PR embeds Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant App as Tauri App
participant Handle as CoreProcessHandle
participant Tokio as Tokio Runtime
participant Core as Embedded Core (openhuman_core)
User->>App: start
App->>Handle: CoreProcessHandle::new(port)
Handle->>Tokio: spawn run_server_embedded(port)
Tokio->>Core: start JSON-RPC server
User->>App: invoke RPC via app
App->>Core: JSON-RPC request
Core-->>App: JSON-RPC response
User->>App: shutdown
App->>Handle: request shutdown / drop
Handle->>Tokio: abort server task
Tokio->>Core: stop & cleanup
App->>App: final cleanup sweep (Unix pkill for stray children)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Review rate limit: 3/5 reviews remaining, refill in 14 minutes and 24 seconds. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
app/src-tauri/src/lib.rs (1)
1512-1519: ⚡ Quick winUnnecessary allocation of args vector.
Since
openhuman_core::run_core_from_argstakes&[String]and the inputargsis already&[String], the intermediateVec<String>allocation is unnecessary.pub fn run_core_from_args(args: &[String]) -> Result<(), String> { - // Core lives in-process: dispatch directly through the linked `openhuman_core` - // library instead of shelling out to a separate binary. The Tauri main() - // routes `OpenHuman core <args>` here so users can still drive the core CLI - // from the bundled app. - let owned: Vec<String> = args.to_vec(); - openhuman_core::run_core_from_args(&owned).map_err(|e| format!("{e:#}")) + openhuman_core::run_core_from_args(args).map_err(|e| format!("{e:#}")) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src-tauri/src/lib.rs` around lines 1512 - 1519, The function run_core_from_args unnecessarily allocates an owned Vec named `owned`; instead pass the incoming slice directly to `openhuman_core::run_core_from_args`. Remove the `owned: Vec<String> = args.to_vec();` allocation and call `openhuman_core::run_core_from_args(args).map_err(|e| format!("{e:#}"))` so no intermediate cloning occurs.app/src-tauri/src/core_process.rs (1)
168-190: 💤 Low valueConsider consolidating duplicate abort logic.
shutdown()andsend_terminate_signal()have identical implementations — both take the lock and abort the task. The only difference is the log message. Consider havingsend_terminate_signaldelegate toshutdownor extracting a shared helper to reduce duplication.+ async fn abort_task(&self, log_context: &str) { + let mut task_guard = self.task.lock().await; + if let Some(task) = task_guard.take() { + log::info!("[core] aborting embedded core server task {log_context}"); + task.abort(); + } + } + /// Stop the embedded server task. Safe to call when nothing is running. pub async fn shutdown(&self) { - let mut task_guard = self.task.lock().await; - if let Some(task) = task_guard.take() { - log::info!("[core] aborting embedded core server task"); - task.abort(); - } + self.abort_task("").await; } /// Synchronous-friendly shutdown for `RunEvent::ExitRequested`. - /// - /// Aborts the embedded server task so any background tokio tasks the - /// server spawned stop driving I/O before CEF's teardown runs. Cheap - /// and non-blocking on the UI thread — `JoinHandle::abort` returns - /// immediately. pub async fn send_terminate_signal(&self) { - let mut task_guard = self.task.lock().await; - if let Some(task) = task_guard.take() { - log::info!("[core] aborting embedded core server task on app shutdown"); - task.abort(); - } + self.abort_task("on app shutdown").await; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src-tauri/src/core_process.rs` around lines 168 - 190, shutdown() and send_terminate_signal() duplicate the same task-abort logic; extract the shared behavior into a single helper (e.g., a private method like abort_embedded_task or similar) that locks self.task, takes the JoinHandle and aborts it, then call that helper from both shutdown() and send_terminate_signal() (or make send_terminate_signal() delegate to shutdown()) while keeping distinct log messages (or accept a log message parameter) so you remove the duplicated lock/abort block while preserving the different log output; refer to the existing methods shutdown, send_terminate_signal, and the self.task field/JoinHandle to locate where to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src-tauri/Cargo.toml`:
- Around line 98-104: The inline comment in the Cargo.toml entry for the
dependency openhuman_core contains a placeholder '#?' for an issue reference;
update that comment by either replacing '#?' with the actual issue number or
removing the placeholder entirely so the comment reads cleanly (for example edit
the comment containing "or CEF helpers behind) by tying the core's lifetime to
the GUI process." to remove or replace the '#?' token).
---
Nitpick comments:
In `@app/src-tauri/src/core_process.rs`:
- Around line 168-190: shutdown() and send_terminate_signal() duplicate the same
task-abort logic; extract the shared behavior into a single helper (e.g., a
private method like abort_embedded_task or similar) that locks self.task, takes
the JoinHandle and aborts it, then call that helper from both shutdown() and
send_terminate_signal() (or make send_terminate_signal() delegate to shutdown())
while keeping distinct log messages (or accept a log message parameter) so you
remove the duplicated lock/abort block while preserving the different log
output; refer to the existing methods shutdown, send_terminate_signal, and the
self.task field/JoinHandle to locate where to change.
In `@app/src-tauri/src/lib.rs`:
- Around line 1512-1519: The function run_core_from_args unnecessarily allocates
an owned Vec named `owned`; instead pass the incoming slice directly to
`openhuman_core::run_core_from_args`. Remove the `owned: Vec<String> =
args.to_vec();` allocation and call
`openhuman_core::run_core_from_args(args).map_err(|e| format!("{e:#}"))` so no
intermediate cloning occurs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 18c8f1d5-1e5f-4b63-9eb2-1f60c0ad262b
⛔ Files ignored due to path filters (1)
app/src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (6)
app/src-tauri/Cargo.tomlapp/src-tauri/src/core_process.rsapp/src-tauri/src/core_process_tests.rsapp/src-tauri/src/core_update.rsapp/src-tauri/src/lib.rsapp/src-tauri/tauri.conf.json
💤 Files with no reviewable changes (2)
- app/src-tauri/tauri.conf.json
- app/src-tauri/src/core_update.rs
Core is now linked into the Tauri shell as a path dep (PR tinyhumansai#1061), so the release pipeline no longer needs to build, stage, sign, or package a standalone openhuman-core binary. Distribution of the core as a headless artifact is Docker-only for now. release.yml: - Drop "Resolve core manifest", "Build sidecar", "Stage sidecar", "Resolve standalone CLI artifact path", "Verify sidecar layout", "Package CLI tarball" (unix), "Package CLI zip" (windows), and "Upload standalone CLI artifacts" steps. - Rename "Re-sign sidecar with hardened runtime and notarize" to "Sign and notarize macOS .app" — the script always signed the whole bundle, the name was historical. - Sentry symbols upload now scans only the Tauri shell's target dir. build.yml / build-windows.yml / test.yml: - Drop the cargo build --bin openhuman-core + stage steps; the Tauri build now pulls in openhuman_core via path dep. - build.yml: drop `pnpm run core:stage` from the inline beforeBuildCommand. e2e-agent-review.yml: - Drop "Stage sidecar next to app binary" — there is no sidecar. release-packages.yml: - Disable trigger (workflow_dispatch only) until a standalone CLI binary is reintroduced. Homebrew / apt / npm / Linux-arm64 tarball jobs all wrapped the openhuman-core binary that no longer exists. tauri.conf.json + package.json: - Drop `pnpm run core:stage` from beforeDevCommand and beforeBuildCommand. - Replace `core:stage` script with a no-op echo so any straggling references in dev-side helpers don't fail; remove the now-unused stage-core-sidecar.mjs and stage-sidecar.sh scripts.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/build-windows.yml:
- Around line 74-75: The "Upload standalone CLI binary" step still references
nonexistent outputs from steps.core-paths (e.g., steps.core-paths.outputs.*),
causing runtime failures; remove that entire upload step or update it to use
existing outputs/artifacts instead. Locate the job step named "Upload standalone
CLI binary" and delete it or replace its use of steps.core-paths.outputs.* with
valid artifact/output names produced earlier in the workflow (or remove the
upload if the core is now embedded), ensuring no remaining references to
core-paths remain.
In @.github/workflows/release.yml:
- Around line 498-507: The current upload step only checks
deps_dir="app/src-tauri/target/${MATRIX_TARGET}/release/deps" and skips if
missing; change it to check both
app/src-tauri/target/${MATRIX_TARGET}/release/deps and
target/${MATRIX_TARGET}/release/deps (or iterate over an array of roots) and
call scripts/upload_sentry_symbols.sh "$VERSION" "$deps_dir" for each existing
path; update the logic around deps_dir and the echo messages so both possible
Tauri output roots are scanned and uploaded (retain MATRIX_TARGET and VERSION
usage and the existing upload script invocation).
In `@app/src-tauri/src/core_process.rs`:
- Around line 94-109: The readiness logic must verify the embedded task before
accepting the port: after your preflight probe detects a listener, acquire and
inspect self.task (the MutexGuard from self.task.lock().await) and ensure it is
Some and not task.is_finished(); if the guard is None or the task has finished
return an Err with context (similar to the existing error messages) instead of
returning Ok(()); only when the guard contains a live task should you drop the
guard and return Ok(()). This prevents attaching to foreign listeners and
ensures restart() will not wait on a port the app never owned.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 00d7f959-c16f-4908-b7ed-1dd7bc1395e1
📒 Files selected for processing (12)
.github/workflows/build-windows.yml.github/workflows/build.yml.github/workflows/e2e-agent-review.yml.github/workflows/release-packages.yml.github/workflows/release.yml.github/workflows/test.ymlapp/package.jsonapp/src-tauri/src/core_process.rsapp/src-tauri/tauri.conf.jsonpackage.jsonscripts/release/stage-sidecar.shscripts/stage-core-sidecar.mjs
💤 Files with no reviewable changes (3)
- package.json
- scripts/release/stage-sidecar.sh
- scripts/stage-core-sidecar.mjs
✅ Files skipped from review due to trivial changes (2)
- .github/workflows/e2e-agent-review.yml
- .github/workflows/release-packages.yml
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src-tauri/tauri.conf.json
| # Core is linked into the Tauri binary as a path dep — no separate | ||
| # sidecar build / stage / path-resolution step needed. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
from pathlib import Path
path = Path(".github/workflows/build-windows.yml")
lines = path.read_text().splitlines()
has_core_paths = any("id: core-paths" in line for line in lines)
print(f"has `id: core-paths`: {has_core_paths}")
for i, line in enumerate(lines, 1):
if "Upload standalone CLI binary" in line or "steps.core-paths.outputs" in line:
print(f"{i}: {line}")
PYRepository: tinyhumansai/openhuman
Length of output: 257
Remove the stale artifact upload step that references a non-existent step.
After dropping the separate core/sidecar build flow, the Upload standalone CLI binary step at lines 119-125 still references steps.core-paths.outputs.*, but there is no core-paths step defined in this workflow. This will cause the step to fail at runtime because the referenced step outputs do not exist.
Suggested fix
- - name: Upload standalone CLI binary
- uses: actions/upload-artifact@v4
- with:
- name: windows-cli
- path: |-
- ${{ steps.core-paths.outputs.core_target_dir }}/${{ steps.core-paths.outputs.core_bin_name }}.exe🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/build-windows.yml around lines 74 - 75, The "Upload
standalone CLI binary" step still references nonexistent outputs from
steps.core-paths (e.g., steps.core-paths.outputs.*), causing runtime failures;
remove that entire upload step or update it to use existing outputs/artifacts
instead. Locate the job step named "Upload standalone CLI binary" and delete it
or replace its use of steps.core-paths.outputs.* with valid artifact/output
names produced earlier in the workflow (or remove the upload if the core is now
embedded), ensuring no remaining references to core-paths remain.
| let mut guard = self.task.lock().await; | ||
| if let Some(task) = guard.as_ref() { | ||
| if task.is_finished() { | ||
| let task = guard.take().expect("checked is_some"); | ||
| drop(guard); | ||
| return match task.await { | ||
| Ok(_) => { | ||
| Err("in-process core server exited before becoming ready".to_string()) | ||
| } | ||
| } | ||
| } | ||
| CoreRunMode::ChildProcess => { | ||
| let mut guard = self.child.lock().await; | ||
| if let Some(child) = guard.as_mut() { | ||
| match child.try_wait() { | ||
| Ok(Some(status)) => { | ||
| return Err(format!("core process exited before ready: {status}")); | ||
| } | ||
| Ok(None) => {} | ||
| Err(e) => { | ||
| return Err(format!("failed checking core process status: {e}")); | ||
| } | ||
| } | ||
| } | ||
| Err(err) => Err(format!( | ||
| "in-process core server task failed before ready: {err}" | ||
| )), | ||
| }; | ||
| } | ||
| } | ||
| tokio::time::sleep(std::time::Duration::from_millis(100)).await; | ||
| tokio::time::sleep(Duration::from_millis(100)).await; |
There was a problem hiding this comment.
Check the embedded task before accepting the port as ready.
Right now the loop returns Ok(()) as soon as anything is listening on the port, and only afterwards checks whether the spawned embedded-core task has already died. If another process grabs the port after the preflight probe, this method can attach to that foreign listener, keep a stale managed task, and make a later restart() wait on a port the app never owned.
Suggested fix
for _ in 0..40 {
- if self.is_rpc_port_open().await {
- log::info!("[core] core rpc became ready at {}", self.rpc_url());
- return Ok(());
- }
-
let mut guard = self.task.lock().await;
if let Some(task) = guard.as_ref() {
if task.is_finished() {
let task = guard.take().expect("checked is_some");
drop(guard);
return match task.await {
Ok(_) => {
Err("in-process core server exited before becoming ready".to_string())
}
Err(err) => Err(format!(
"in-process core server task failed before ready: {err}"
)),
};
}
}
+ drop(guard);
+
+ if self.is_rpc_port_open().await {
+ log::info!("[core] core rpc became ready at {}", self.rpc_url());
+ return Ok(());
+ }
tokio::time::sleep(Duration::from_millis(100)).await;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src-tauri/src/core_process.rs` around lines 94 - 109, The readiness logic
must verify the embedded task before accepting the port: after your preflight
probe detects a listener, acquire and inspect self.task (the MutexGuard from
self.task.lock().await) and ensure it is Some and not task.is_finished(); if the
guard is None or the task has finished return an Err with context (similar to
the existing error messages) instead of returning Ok(()); only when the guard
contains a live task should you drop the guard and return Ok(()). This prevents
attaching to foreign listeners and ensures restart() will not wait on a port the
app never owned.
Resolves conflicts with the core RPC auth (tinyhumansai#881) and "staging in debug profile" (tinyhumansai#1044) changes that landed on main while this PR was open. CodeRabbit nits applied as part of the merge: - Cargo.toml: replace `#?` placeholder with the PR reference. - core_process.rs: dedupe shutdown/send_terminate_signal via a shared abort_task() helper. - lib.rs::run_core_from_args: drop the redundant args.to_vec() allocation and pass the slice through directly. Conflict resolution summary: app/src-tauri/src/core_process.rs - Keep the in-process / no-sidecar architecture from this PR (no `Child`, no `core_bin`, no `CoreRunMode`). - Pull in the auth token plumbing from main: generate_rpc_token, CURRENT_RPC_TOKEN, and an `rpc_token` field on the handle. Set OPENHUMAN_CORE_TOKEN as a process-global env var before spawning the embedded server so the same-process tokio task reads exactly what a child sidecar would have received via Command::env. Publish to CURRENT_RPC_TOKEN only after spawn, matching upstream's invariant that the global only advertises a token a running listener has actually accepted. - Note in the harness-attach fast-path warning that an external listener won't see this process's token — Tauri-side authenticated calls will 401 unless the harness was started with the same OPENHUMAN_CORE_TOKEN. app/src-tauri/src/core_process_tests.rs - Drop run_mode / default_core_bin / same_executable_path / sidecar-staging tests (functions removed in this PR). - Keep upstream's token-related tests, simplified for the slim `CoreProcessHandle::new(port)` constructor. app/src-tauri/src/lib.rs - Module list: keep `mod core_rpc` (auth helper used by various Tauri commands), drop `mod core_update` — core_update.rs is deleted with the rest of the sidecar update subsystem. - check_core_update: keep the no-op stub; the frontend wrapper keeps working via `tauri-plugin-updater`. app/src-tauri/src/core_update.rs - Stays deleted. Core ships with the desktop app now; per-binary update is via tauri-plugin-updater. .github/workflows/release.yml - Drop upstream's resolve-core-paths / build-sidecar / stage-sidecar / cli-paths block; sidecar build is gone. - Collapse the split core/Tauri Sentry symbols upload into a single step: linked openhuman_core symbols now live under the Tauri shell's target dir. - Preserve upstream's staging→debug profile path expression in the bundle artifact upload. app/src-tauri/Cargo.lock - Regenerated against the merged Cargo.toml.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src-tauri/src/core_process.rs (1)
116-116: 💤 Low value
std::env::set_varin async context has soundness concerns.
std::env::set_varis not thread-safe; calling it while other threads may be reading environment variables can cause data races. Since Rust 1.66, this is formally deprecated in multi-threaded contexts. While the risk here is low (called once during startup, before heavy concurrency), consider passing the token directly torun_server_embeddedas an argument if the API supports it.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src-tauri/src/core_process.rs` at line 116, Avoid using std::env::set_var at runtime to set OPENHUMAN_CORE_TOKEN due to thread-safety concerns; instead modify the call site to pass the token directly into run_server_embedded (or an equivalent initialization function) so the token comes from self.rpc_token without mutating global environment state. Locate the usage of std::env::set_var("OPENHUMAN_CORE_TOKEN", self.rpc_token.as_str()) and change the server-start call (run_server_embedded) to accept an extra parameter for the RPC token (or add a construction/Config object carrying self.rpc_token) and thread that token through to where the server needs it, removing the environment write. Ensure any tests or callers are updated to provide the token argument.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/src-tauri/src/core_process.rs`:
- Line 116: Avoid using std::env::set_var at runtime to set OPENHUMAN_CORE_TOKEN
due to thread-safety concerns; instead modify the call site to pass the token
directly into run_server_embedded (or an equivalent initialization function) so
the token comes from self.rpc_token without mutating global environment state.
Locate the usage of std::env::set_var("OPENHUMAN_CORE_TOKEN",
self.rpc_token.as_str()) and change the server-start call (run_server_embedded)
to accept an extra parameter for the RPC token (or add a construction/Config
object carrying self.rpc_token) and thread that token through to where the
server needs it, removing the environment write. Ensure any tests or callers are
updated to provide the token argument.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2cac3e01-89f5-4f12-96a2-b4279be81f98
⛔ Files ignored due to path filters (1)
app/src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
.github/workflows/release.ymlapp/src-tauri/Cargo.tomlapp/src-tauri/src/core_process.rsapp/src-tauri/src/core_process_tests.rsapp/src-tauri/src/lib.rs
✅ Files skipped from review due to trivial changes (1)
- app/src-tauri/Cargo.toml
- React: drop the `beforeSend` opt-in queue and auto-forward sanitized events to `openhuman-react`. PII / breadcrumbs / request bodies / frame-level locals + source snippets are still stripped, and the `isAnalyticsEnabled()` consent check now gates `beforeSend`. Tag every event with `surface: "react"`. Add a `VITE_SENTRY_SMOKE_TEST` one-shot trigger for verifying the pipeline end-to-end. - Delete `errorReportQueue` + `ErrorReportNotification` (the user-opt-in scaffolding) and the OAuth stale-app-version `enqueueError` call — replaced with a direct `Sentry.captureMessage` plus `console.warn`. - Workflow: since tinyhumansai#1061 the core lives in-process inside the Tauri shell binary (one process → one `sentry::init` → one hub), so all Rust events from the desktop build route to `openhuman-tauri`. Repoint the Rust DIF upload step in `release.yml` and the Tauri-shell upload in `release-staging.yml` to `vars.SENTRY_PROJECT_TAURI`. Standalone `openhuman-core` CLI binary (built by the staging sidecar step and by `release-packages.yml`) still uploads to `vars.SENTRY_PROJECT_CORE`.
…ent (tinyhumansai#985) The shipped `.app` was missing two macOS-required pieces for an app that sends Apple Events: * `NSAppleEventsUsageDescription` in `Info.plist` — without this key the OS-level consent dialog that fires on first AE attempt has no descriptive text and renders as the broken-looking error popup reported in tinyhumansai#985. With it present, macOS shows a proper "OpenHuman wants to control System Events" dialog with the user- facing text from this string and Allow / Don't Allow buttons; the decision is then sticky per-target. * `com.apple.security.automation.apple-events` entitlement in `entitlements.sidecar.plist` — under the Hardened Runtime in signed DMG builds, AE calls are blocked outright before the consent dialog can render unless the calling app holds this entitlement. `entitlements.sidecar.plist` is the file wired at signing time per `tauri.conf.json` `bundle.macOS.entitlements`, so adding it here covers the main `OpenHuman` bin (PR tinyhumansai#1061 made the core in-process, so there is no separate sidecar to entitle). Refs tinyhumansai#985. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(remotion): Ghosty character library with transparent MOV variants (tinyhumansai#1059) Co-authored-by: WOZCODE <contact@withwoz.com> * feat(composio/gmail): sync into memory tree (Slack-parity) (tinyhumansai#1056) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(scheduler-gate): throttle background AI on battery / busy CPU (tinyhumansai#1062) * fix(core,cef): run core in-process and stop orphaning CEF helpers on Cmd+Q (tinyhumansai#1061) * ci: add dedicated staging release workflow (tinyhumansai#1066) * fix(sentry): Rust source context + per-release deploy marker (tinyhumansai#405) (tinyhumansai#1067) * fix(welcome): re-enable OAuth buttons with focus/timeout recovery (tinyhumansai#1049) (tinyhumansai#1069) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(dependencies): update pnpm-lock.yaml and Cargo.lock for package… (tinyhumansai#1082) * fix(onboarding): personalize welcome agent greeting with user identity (tinyhumansai#1078) * fix(chat): make agent message bubbles fit content width (tinyhumansai#1083) * Feat/dmg checks (tinyhumansai#1084) * fix(linux): Add X11 platform flags to .deb package launcher (tinyhumansai#1087) Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> * fix(sentry): auto-send React events; collapse core→tauri for desktop (tinyhumansai#1086) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(cef): run blank reload guard on the CEF UI thread (tinyhumansai#1092) * fix(app): reload webview instead of restart_app in dev mode (tinyhumansai#1068) (tinyhumansai#1071) * fix(linux): deliver X11 ozone flags via custom .desktop template (tinyhumansai#1091) * fix(webview-accounts): retry data-dir purge so CEF handle race doesn't leak cookies (tinyhumansai#1076) (tinyhumansai#1081) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(webview/slack): media perms + deep-link isolation (tinyhumansai#1074) (tinyhumansai#1080) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * ci(release): split staging vs production workflows; promote staging tags (tinyhumansai#1094) * Update release-staging.yml (tinyhumansai#1097) * chore(staging): v0.53.5 * chore(staging): v0.53.6 * ci(staging): cut staging from main; add act local-debug helper (tinyhumansai#1099) * chore(staging): v0.53.7 * fix(ci): correct sentry-cli download URL and trap scope (tinyhumansai#1100) * chore(staging): v0.53.8 * feat(chat): forward thread_id to backend for KV cache locality (tinyhumansai#1095) * fix(ci): bump pinned sentry-cli to 3.4.1 (2.34.2 was never published) (tinyhumansai#1102) * chore(staging): v0.53.9 * fix(ci): drop bash trap in upload_sentry_symbols.sh; inline cleanup (tinyhumansai#1103) * chore(staging): v0.53.10 * refactor(session): flatten session_raw/, switch md to YYYY_MM_DD (tinyhumansai#1098) * Add full Composio managed-auth toolkit catalog (tinyhumansai#1093) * ci: add diff-aware 80% coverage gate (Vitest + cargo-llvm-cov) (tinyhumansai#1104) * feat(scripts): pnpm work + pnpm debug for agent-driven workflows (tinyhumansai#1105) * ci: pull pnpm into CI image, drop redundant setup steps (tinyhumansai#1107) * docs: add Cursor Cloud specific instructions to AGENTS.md (tinyhumansai#1106) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * chore(staging): v0.53.11 * docs: surface 80% coverage gate and scripts/debug runners (tinyhumansai#1108) * feat(app): show Composio integrations as sorted icon grid on Skills (tinyhumansai#1109) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * feat(composio): client-side trigger enable/disable toggles (tinyhumansai#1110) * feat(skills): channels grid + integrations card polish; tolerant Composio trigger decode (tinyhumansai#1112) * chore(staging): v0.53.12 * feat(home): early-bird banner + assistant→agent terminology (tinyhumansai#1113) * feat(updater): in-app auto-update with auto-download + restart prompt (tinyhumansai#677) (tinyhumansai#1114) * chore(claude): add ship-and-babysit slash command (tinyhumansai#1115) * feat(home): EarlyBirdyBanner + agent terminology + LinkedIn enrichment model pin (tinyhumansai#1118) * fix(chat): single onboarding thread in sidebar after wizard (tinyhumansai#1116) Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> * fix: filter out global namespace from citation chips (tinyhumansai#1124) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> * feat(nav): enable Memory tab in BottomTabBar (tinyhumansai#1125) * feat(memory): singleton ingestion + status RPC + UI pill (tinyhumansai#1126) * feat(human): mascot tab with viseme-driven lipsync (staging only) (tinyhumansai#1127) * Fix CEF zombie processes on full app close and restart (tinyhumansai#1128) Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * Update issue templates for GitHub issue types (tinyhumansai#1146) * feat(human): expand mascot expressions and tighten reply-speech state machine (tinyhumansai#1147) * feat(memory): ingestion pipeline + tree-architecture docs + ops/schemas split (tinyhumansai#1142) * feat(threads): surface live subagent work in parent thread (tinyhumansai#1122) (tinyhumansai#1159) * fix(human): keep mascot mouth animating when TTS ships no viseme data (tinyhumansai#1160) * feat(composio): consume backend markdownFormatted for LLM output (tinyhumansai#1165) * fix(subagent): lazy-register toolkit actions filtered out of fuzzy top-K (tinyhumansai#1162) * feat(memory): user-facing long-term memory window preset (tinyhumansai#1137) (tinyhumansai#1161) * fix(tauri-shell): proactively kill stale openhuman RPC on startup (tinyhumansai#1166) * chore(staging): v0.53.13 * fix(composio): per-action tool consumes backend markdownFormatted (tinyhumansai#1167) * fix(threads): persist selectedThreadId across reloads (tinyhumansai#1168) * feat(memory_tree): switch embed model to bge-m3 (1024-dim, 8K context) (tinyhumansai#1174) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(agent): drop redundant [Memory context] recall injection (tinyhumansai#1173) * chore(memory_tree): drop body-read timeouts on Ollama HTTP calls (tinyhumansai#1171) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(transcript): emit thread_id + fix orchestrator missing cost (tinyhumansai#1169) * fix(composio/gmail): phase out html2md, prefer text/plain MIME part (tinyhumansai#1170) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): markdown output for internal tool results (tinyhumansai#1172) * feat(security): enforce prompt-injection guard before model and tool execution (tinyhumansai#1175) * fix(cef): popup paint dies after first frame — skip blank-page guard for popups (tinyhumansai#1079) (tinyhumansai#1182) Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> * chore(sentry): rename OPENHUMAN_SENTRY_DSN → OPENHUMAN_CORE_SENTRY_DSN (tinyhumansai#1186) * feat(remotion): add yellow mascot character with all animation variants (tinyhumansai#1193) Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(composio): hide raw connection ID, derive friendly label (tinyhumansai#1153) (tinyhumansai#1185) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(windows): align install.ps1 MSI with per-machine scope (tinyhumansai#913) (tinyhumansai#1187) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tauri): deterministic CEF teardown on full app close (tinyhumansai#1120) (tinyhumansai#1189) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(composio): cap Gmail HTML body before strip (crash mitigation) (tinyhumansai#1191) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(auth): stop stale chat threads after signup (tinyhumansai#1192) Co-authored-by: Cursor <cursoragent@cursor.com> * feat(sentry): staging-only "Trigger Sentry Test" button (tinyhumansai#1072) (tinyhumansai#1183) * chore(staging): v0.53.14 * chore(staging): v0.53.15 * feat(composio): format trigger slugs into human-readable labels (tinyhumansai#1129) (tinyhumansai#1179) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(ui): hide unsupported permission UI on non-macOS for Screen Intelligence (tinyhumansai#1194) Co-authored-by: Cursor <cursoragent@cursor.com> * chore(tauri-shell): retire embedded Gmail webview-account flow (tinyhumansai#1181) * feat(onboarding): replace welcome-agent bot with react-joyride walkthrough (tinyhumansai#1180) * chore(release): v0.53.16 * fix(threads): preserve selectedThreadId on cold-boot identity hydration (tinyhumansai#1196) * feat(core): version/shutdown/update RPCs + mid-thread integration refresh (tinyhumansai#1195) * fix(mascot): swap to yellow mascot via @remotion/player (tinyhumansai#1200) * feat(memory_tree): cloud-default LLM, queue priority, entity filter, Memory tab UI (tinyhumansai#1198) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Persist turn state + restore conversation history on cold-boot (tinyhumansai#1202) * feat(mascot): floating desktop mascot via native NSPanel + WKWebView (macOS) (tinyhumansai#1203) * fix(memory/tree): emit summary children as Obsidian wikilinks (tinyhumansai#1210) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): coding-harness baseline primitives (tinyhumansai#1205) (tinyhumansai#1208) * docs: add Codex PR checklist for remote agents --------- Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: WOZCODE <contact@withwoz.com> Co-authored-by: sanil-23 <sanil@vezures.xyz> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Co-authored-by: CodeGhost21 <164498022+CodeGhost21@users.noreply.github.com> Co-authored-by: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Co-authored-by: Mega Mind <146339422+M3gA-Mind@users.noreply.github.com> Co-authored-by: Gaurang Patel <ptelgm.yt@gmail.com> Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> Co-authored-by: Steven Enamakel's Droid <enamakel.agent@tinyhumans.ai> Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Co-authored-by: Neil <neil@maha.xyz> Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: obchain <167975049+obchain@users.noreply.github.com> Co-authored-by: Jwalin Shah <jshah1331@gmail.com>
Bilateral mirror of the core binary's before_send filter — since tinyhumansai#1061 the Tauri shell runs the core in-process, so the same is_session_expired_event guard must run here too. Drops OPENHUMAN-TAURI-25 / -1Q / -27 / -1G regardless of which surface captured the event. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ore_send wire Recreates PR tinyhumansai#1719's surface against the current upstream/main (the prior branch was 20+ commits behind and `SessionExpired` enum + matcher `is_session_expired_message` + `expected_error_kind` arm + per-kind `report_expected_message` arm all landed via tinyhumansai#1763, so the original classifier-extension commits were duplicates). Three remaining unique deltas land here in one commit: - `is_session_expired_event(event)` in `src/core/observability.rs` — tag-and-body classifier that drops `(domain=llm_provider|backend_api|rpc) + status=401 + body matches is_session_expired_message` shapes, plus the pre-flight rpc dispatcher path that has no status tag. Composio's OAuth-state 401 is intentionally excluded — that's actionable and must reach Sentry. - Bilateral before_send wire in `src/main.rs` (core binary) and `app/src-tauri/src/lib.rs` (Tauri shell — since tinyhumansai#1061 it links the core in-process, so any session-expired event captured by either surface lands in the same Sentry client and must be filtered identically). Both filters log only `event.event_id` per CR feedback from PR tinyhumansai#1719 — `event.message` carries the raw backend response body which CLAUDE.md forbids from local logs. - Smoke test runtime path in `tests/observability_smoke.rs` — imports the new classifier and threads it into the same `before_send` chain shape the real binary installs, so the runtime drop is covered end- to-end alongside the existing budget / transient / updater filters. Drops OPENHUMAN-TAURI-25 / -1Q / -27 / -1G (~185 events/day combined). The original PR's emit-site demotions (compatible.rs / authed_json / agent run_single) were superseded by tinyhumansai#1763 — leaving them out keeps this PR scoped to the before_send defense-in-depth that tinyhumansai#1763 didn't ship. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary - Adds a **MCP Server** panel under Settings → Developer Options so users can configure external MCP clients without hand-editing JSON files - New Tauri commands `mcp_resolve_binary_path` (returns binary path + OS) and `mcp_open_client_config` (opens the client's config file in the system editor) - Generates correct per-client JSON snippets for Claude Desktop, Cursor, Codex, and Zed — OS-aware config file paths for macOS, Windows, and Linux - Copy-to-clipboard and "Open Config File" (Tauri-only) buttons eliminate the manual setup steps that were blocking non-developer adoption ## Problem - The `openhuman-core mcp` stdio server ships 10 memory/tree tools but has zero UI surface — users must locate the binary, find the per-client config file path, and hand-write the JSON - Conversion drops to near-zero outside of developers; this is the bottleneck on MCP adoption for the features already merged in #1760, #1790, #1974 ## Solution - `app/src-tauri/src/mcp_commands.rs`: two new Tauri shell commands with OS-aware path resolution, auto-create config dirs/files if absent, and platform-specific `open`/`explorer`/`xdg-open` dispatch - `McpServerPanel.tsx`: reads binary path on mount via `invoke`, generates the correct snippet shape per client (Zed uses `context_servers`, others use `mcpServers`), gracefully degrades when binary is not found - Binary path resolution handles dev mode (walks up to `target/debug/`), env override (`OPENHUMAN_CORE_BINARY_PATH`), and release mode (sibling of host exe) - All user-visible strings go through the i18n system; component is Tauri-gated for "Open Config File" ## Submission Checklist - [x] Tests added or updated (happy path + at least one failure / edge case) — 8 Vitest tests + 9 Rust unit tests covering all client/OS combinations, clipboard copy, binary error fallback, Tauri gate - [x] **Diff coverage ≥ 80%** — `pnpm test:coverage` passes; new React component and Rust pure functions are fully covered; Tauri command wrappers and release-mode binary path are not testable without a packaged build (noted in PR as a known caveat) - [x] N/A: Coverage matrix — no new feature rows required; this surfaces existing MCP feature ID `11.1.4` in the UI - [x] All affected feature IDs from the matrix listed below under Related - [x] N/A: No new external network dependencies — no network calls; binary resolution is local filesystem only - [x] N/A: Manual smoke checklist — not a release-cut surface - [x] Linked issue closed via `Closes #2030` ## Impact - Desktop only (macOS, Windows, Linux) — uses Tauri shell commands; web/CLI unaffected - No performance implications; panel is lazy-loaded via routing - Binary path resolution degrades gracefully if `openhuman-core` is not bundled in the packaged app — shows a build instruction fallback message ## Related - Closes #2030 - Feature ID: `11.1.4` (MCP stdio server) - Builds on: #1760, #1790, #1974 - Follow-up: verify `openhuman-core` binary is included in the packaged `.app` bundle (sidecar was removed in #1061; if the binary is not bundled the panel will show the fallback message in production) --- ## AI Authored PR Metadata (required for Codex/Linear PRs) ### Linear Issue - Key: N/A - URL: N/A ### Commit & Branch - Branch: `feat/mcp-settings-panel` - Commit SHA: 85e091d ### Validation Run - [x] `pnpm --filter openhuman-app format:check` - [x] `pnpm typecheck` - [x] Focused tests: `pnpm debug unit McpServerPanel.test.tsx` — 8/8 passed - [x] Rust fmt/check (if changed): `cargo fmt --check` + `cargo check --manifest-path app/src-tauri/Cargo.toml` — clean - [x] Tauri fmt/check (if changed): included above ### Validation Blocked - `command:` N/A - `error:` N/A - `impact:` N/A ### Behavior Changes - Intended behavior change: Adds MCP Server panel to Settings → Developer Options with snippet generator and config file opener - User-visible effect: Users can now configure Claude Desktop, Cursor, Codex, or Zed to use OpenHuman's MCP server in a few clicks instead of hand-editing JSON ### Parity Contract - Legacy behavior preserved: No existing behavior changed; purely additive - Guard/fallback/dispatch parity checks: `isTauri()` guard on "Open Config File" button; binary-not-found degrades to placeholder with build instructions ### Duplicate / Superseded PR Handling - Duplicate PR(s): None - Canonical PR: This PR - Resolution: N/A <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an "MCP Server" settings panel under Developer Options with tabs for multiple clients (Claude Desktop, Cursor, Codex, Zed) * Shows resolved MCP/OpenHuman binary status, generates client-specific JSON snippets, copy-to-clipboard, and an "Open Config File" action when available * **Tests** * Added UI tests for rendering, snippet content, copy behavior, binary-failure fallback, and open-config action * **Localization** * Added translations for the MCP Server UI across many languages <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2355?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: M3gA-Mind <megamind@mahadao.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
## Summary - Adds a **MCP Server** panel under Settings → Developer Options so users can configure external MCP clients without hand-editing JSON files - New Tauri commands `mcp_resolve_binary_path` (returns binary path + OS) and `mcp_open_client_config` (opens the client's config file in the system editor) - Generates correct per-client JSON snippets for Claude Desktop, Cursor, Codex, and Zed — OS-aware config file paths for macOS, Windows, and Linux - Copy-to-clipboard and "Open Config File" (Tauri-only) buttons eliminate the manual setup steps that were blocking non-developer adoption ## Problem - The `openhuman-core mcp` stdio server ships 10 memory/tree tools but has zero UI surface — users must locate the binary, find the per-client config file path, and hand-write the JSON - Conversion drops to near-zero outside of developers; this is the bottleneck on MCP adoption for the features already merged in tinyhumansai#1760, tinyhumansai#1790, tinyhumansai#1974 ## Solution - `app/src-tauri/src/mcp_commands.rs`: two new Tauri shell commands with OS-aware path resolution, auto-create config dirs/files if absent, and platform-specific `open`/`explorer`/`xdg-open` dispatch - `McpServerPanel.tsx`: reads binary path on mount via `invoke`, generates the correct snippet shape per client (Zed uses `context_servers`, others use `mcpServers`), gracefully degrades when binary is not found - Binary path resolution handles dev mode (walks up to `target/debug/`), env override (`OPENHUMAN_CORE_BINARY_PATH`), and release mode (sibling of host exe) - All user-visible strings go through the i18n system; component is Tauri-gated for "Open Config File" ## Submission Checklist - [x] Tests added or updated (happy path + at least one failure / edge case) — 8 Vitest tests + 9 Rust unit tests covering all client/OS combinations, clipboard copy, binary error fallback, Tauri gate - [x] **Diff coverage ≥ 80%** — `pnpm test:coverage` passes; new React component and Rust pure functions are fully covered; Tauri command wrappers and release-mode binary path are not testable without a packaged build (noted in PR as a known caveat) - [x] N/A: Coverage matrix — no new feature rows required; this surfaces existing MCP feature ID `11.1.4` in the UI - [x] All affected feature IDs from the matrix listed below under Related - [x] N/A: No new external network dependencies — no network calls; binary resolution is local filesystem only - [x] N/A: Manual smoke checklist — not a release-cut surface - [x] Linked issue closed via `Closes tinyhumansai#2030` ## Impact - Desktop only (macOS, Windows, Linux) — uses Tauri shell commands; web/CLI unaffected - No performance implications; panel is lazy-loaded via routing - Binary path resolution degrades gracefully if `openhuman-core` is not bundled in the packaged app — shows a build instruction fallback message ## Related - Closes tinyhumansai#2030 - Feature ID: `11.1.4` (MCP stdio server) - Builds on: tinyhumansai#1760, tinyhumansai#1790, tinyhumansai#1974 - Follow-up: verify `openhuman-core` binary is included in the packaged `.app` bundle (sidecar was removed in tinyhumansai#1061; if the binary is not bundled the panel will show the fallback message in production) --- ## AI Authored PR Metadata (required for Codex/Linear PRs) ### Linear Issue - Key: N/A - URL: N/A ### Commit & Branch - Branch: `feat/mcp-settings-panel` - Commit SHA: 85e091d ### Validation Run - [x] `pnpm --filter openhuman-app format:check` - [x] `pnpm typecheck` - [x] Focused tests: `pnpm debug unit McpServerPanel.test.tsx` — 8/8 passed - [x] Rust fmt/check (if changed): `cargo fmt --check` + `cargo check --manifest-path app/src-tauri/Cargo.toml` — clean - [x] Tauri fmt/check (if changed): included above ### Validation Blocked - `command:` N/A - `error:` N/A - `impact:` N/A ### Behavior Changes - Intended behavior change: Adds MCP Server panel to Settings → Developer Options with snippet generator and config file opener - User-visible effect: Users can now configure Claude Desktop, Cursor, Codex, or Zed to use OpenHuman's MCP server in a few clicks instead of hand-editing JSON ### Parity Contract - Legacy behavior preserved: No existing behavior changed; purely additive - Guard/fallback/dispatch parity checks: `isTauri()` guard on "Open Config File" button; binary-not-found degrades to placeholder with build instructions ### Duplicate / Superseded PR Handling - Duplicate PR(s): None - Canonical PR: This PR - Resolution: N/A <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an "MCP Server" settings panel under Developer Options with tabs for multiple clients (Claude Desktop, Cursor, Codex, Zed) * Shows resolved MCP/OpenHuman binary status, generates client-specific JSON snippets, copy-to-clipboard, and an "Open Config File" action when available * **Tests** * Added UI tests for rendering, snippet content, copy behavior, binary-failure fallback, and open-config action * **Localization** * Added translations for the MCP Server UI across many languages <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2355?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: M3gA-Mind <megamind@mahadao.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
## Summary - Adds a **MCP Server** panel under Settings → Developer Options so users can configure external MCP clients without hand-editing JSON files - New Tauri commands `mcp_resolve_binary_path` (returns binary path + OS) and `mcp_open_client_config` (opens the client's config file in the system editor) - Generates correct per-client JSON snippets for Claude Desktop, Cursor, Codex, and Zed — OS-aware config file paths for macOS, Windows, and Linux - Copy-to-clipboard and "Open Config File" (Tauri-only) buttons eliminate the manual setup steps that were blocking non-developer adoption ## Problem - The `openhuman-core mcp` stdio server ships 10 memory/tree tools but has zero UI surface — users must locate the binary, find the per-client config file path, and hand-write the JSON - Conversion drops to near-zero outside of developers; this is the bottleneck on MCP adoption for the features already merged in tinyhumansai#1760, tinyhumansai#1790, tinyhumansai#1974 ## Solution - `app/src-tauri/src/mcp_commands.rs`: two new Tauri shell commands with OS-aware path resolution, auto-create config dirs/files if absent, and platform-specific `open`/`explorer`/`xdg-open` dispatch - `McpServerPanel.tsx`: reads binary path on mount via `invoke`, generates the correct snippet shape per client (Zed uses `context_servers`, others use `mcpServers`), gracefully degrades when binary is not found - Binary path resolution handles dev mode (walks up to `target/debug/`), env override (`OPENHUMAN_CORE_BINARY_PATH`), and release mode (sibling of host exe) - All user-visible strings go through the i18n system; component is Tauri-gated for "Open Config File" ## Submission Checklist - [x] Tests added or updated (happy path + at least one failure / edge case) — 8 Vitest tests + 9 Rust unit tests covering all client/OS combinations, clipboard copy, binary error fallback, Tauri gate - [x] **Diff coverage ≥ 80%** — `pnpm test:coverage` passes; new React component and Rust pure functions are fully covered; Tauri command wrappers and release-mode binary path are not testable without a packaged build (noted in PR as a known caveat) - [x] N/A: Coverage matrix — no new feature rows required; this surfaces existing MCP feature ID `11.1.4` in the UI - [x] All affected feature IDs from the matrix listed below under Related - [x] N/A: No new external network dependencies — no network calls; binary resolution is local filesystem only - [x] N/A: Manual smoke checklist — not a release-cut surface - [x] Linked issue closed via `Closes tinyhumansai#2030` ## Impact - Desktop only (macOS, Windows, Linux) — uses Tauri shell commands; web/CLI unaffected - No performance implications; panel is lazy-loaded via routing - Binary path resolution degrades gracefully if `openhuman-core` is not bundled in the packaged app — shows a build instruction fallback message ## Related - Closes tinyhumansai#2030 - Feature ID: `11.1.4` (MCP stdio server) - Builds on: tinyhumansai#1760, tinyhumansai#1790, tinyhumansai#1974 - Follow-up: verify `openhuman-core` binary is included in the packaged `.app` bundle (sidecar was removed in tinyhumansai#1061; if the binary is not bundled the panel will show the fallback message in production) --- ## AI Authored PR Metadata (required for Codex/Linear PRs) ### Linear Issue - Key: N/A - URL: N/A ### Commit & Branch - Branch: `feat/mcp-settings-panel` - Commit SHA: 85e091d ### Validation Run - [x] `pnpm --filter openhuman-app format:check` - [x] `pnpm typecheck` - [x] Focused tests: `pnpm debug unit McpServerPanel.test.tsx` — 8/8 passed - [x] Rust fmt/check (if changed): `cargo fmt --check` + `cargo check --manifest-path app/src-tauri/Cargo.toml` — clean - [x] Tauri fmt/check (if changed): included above ### Validation Blocked - `command:` N/A - `error:` N/A - `impact:` N/A ### Behavior Changes - Intended behavior change: Adds MCP Server panel to Settings → Developer Options with snippet generator and config file opener - User-visible effect: Users can now configure Claude Desktop, Cursor, Codex, or Zed to use OpenHuman's MCP server in a few clicks instead of hand-editing JSON ### Parity Contract - Legacy behavior preserved: No existing behavior changed; purely additive - Guard/fallback/dispatch parity checks: `isTauri()` guard on "Open Config File" button; binary-not-found degrades to placeholder with build instructions ### Duplicate / Superseded PR Handling - Duplicate PR(s): None - Canonical PR: This PR - Resolution: N/A <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an "MCP Server" settings panel under Developer Options with tabs for multiple clients (Claude Desktop, Cursor, Codex, Zed) * Shows resolved MCP/OpenHuman binary status, generates client-specific JSON snippets, copy-to-clipboard, and an "Open Config File" action when available * **Tests** * Added UI tests for rendering, snippet content, copy behavior, binary-failure fallback, and open-config action * **Localization** * Added translations for the MCP Server UI across many languages <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2355?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: M3gA-Mind <megamind@mahadao.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Summary
Two related bugs on app exit: the spawned `openhuman-core` sidecar and CEF helper processes (renderers / GPU / utility) were left running after Cmd+Q. Phase 1 removes the sidecar architecture entirely and runs the core in-process; phase 2 makes the CEF teardown deterministic so leftover helpers don't get re-parented to launchd.
Changes
Phase 1 — core in-process (commit `d700f538`)
The Tauri shell now links `openhuman_core` as a path dep and spawns its HTTP/JSON-RPC server as a tokio task inside the GUI process. Core's lifetime is tied to the host so `RunEvent::ExitRequested` ⇒ `task.abort()` ⇒ no orphan.
Phase 2 — CEF teardown (commit `2cb6607f`)
The vendored `tauri-runtime-cef` already calls `cef::shutdown()` after `RunEvent::Exit`, but two gaps remained:
Net diff
```
7 files changed, 3100 insertions(+), 1853 deletions(-)
delete mode 100644 app/src-tauri/src/core_update.rs
```
The big +inserts number is `Cargo.lock` reconciliation from linking `openhuman_core`.
Test plan
Caveats
Summary by CodeRabbit