{"owner":"tinyhumansai","repo":"openhuman","hasSkills":true,"totalSkillsCount":6,"totalTokensCount":28916,"categories":["root-instruction","claude-rule","mcp-config","marketplace","plugin-manifest"],"hasMcp":true,"mcpConfig":{"mcpServers":{"openhuman":{"command":"npx","args":["-y","@modelcontextprotocol/server-openhuman"]}}},"found":["AGENTS.md","CLAUDE.md","llms.txt",".cursor/mcp.json",".claude-plugin/marketplace.json",".claude-plugin/plugin.json"],"skills":{"AGENTS.md":"# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r","CLAUDE.md":"# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r","llms.txt":"# OpenHuman Embed\n\n> Embed OpenHuman runtimes and independently configured agents in Rust applications.\n\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embed/README.md): `openhuman-embed` runs the OpenHuman core inside your Rust program. Your host owns the user interface, server transport and authentication flow; the core owns agent turns, memory, tools, approvals and execution policy. Calling its typed facades does not require a JSON-RPC server or the desktop shell.\n- [Embedding API reference](docs/gitbooks/en/developing/embed/api-reference.md): Generate usable local Rust API documentation with `cargo doc -p openhuman-embed --no-deps --open`. Include `--features mcp,skills,channels,storage-sqlite` for the facade gates used by the offline examples, or select the feature set your host actually builds. For all exported optional surfaces use `--all-features`.\n- [Embedding architecture](docs/gitbooks/en/developing/embed/architecture.md): Embed is the library facade above `openhuman-core`. The TinyHumans layer installs the backend SDK transport and owns product login/session behavior; the RPC layer adds JSON-RPC, server listeners and shipped host boot. A Rust embedder can stay at the facade when it owns those concerns itself.\n- [Concepts](docs/gitbooks/en/developing/embed/concepts/README.md): Start with Runtime and agents to understand which settings are shared. Then follow the lifecycle of a request through provider selection, tools, approvals and its durable session. The scope tables distinguish runtime composition from the state a host must isolate per agent or profile.\n- [Access and approvals](docs/gitbooks/en/developing/embed/concepts/access-approvals.md): `Access` groups permission level, origin, standing grants and trusted roots. Choose an origin and tier together: a trusted local host request and an external channel message are different security contexts. `Access::full()` configures both access fields. A sandbox is an additional execution boundary, configured through `AgentDefinitionSpec`.\n- [Agents](docs/gitbooks/en/developing/embed/concepts/agents.md): Register an `AgentSpec` with `Runtime::agent`. Its ID selects the agent's home, durable session identity and addressed cron/channel work. An `Agent` clone refers to the same instance; it does not copy state or start another core. Runtime-wide credentials remain shared, so two agents are not two authenticated customers.\n- [Channels](docs/gitbooks/en/developing/embed/concepts/channels.md): `Runtime::channels` creates listeners addressed to a live agent ID. A listener owns its transport task; dropping or stopping it ends that listener. The agent supplies the prompt, provider and tools used for every incoming message instead of handing messages to an operator agent.\n- [Completer](docs/gitbooks/en/developing/embed/concepts/completer.md): Use `Completer` for one-shot text or structured generation when you do not need an agent, its tool loop or persistent transcript. It takes an explicit route and a `CompletionRequest`; the caller supplies the model and messages for every request.\n- [Cron](docs/gitbooks/en/developing/embed/concepts/cron.md): The typed `Cron` facade creates named jobs with `JobSpec` and `JobSchedule`. `upsert` preserves a job's identity when the same name is configured again; `runs` exposes execution history. An agent-targeted job resolves the configured agent ID and executes with that agent's provider, prompt, tools and derived context.\n- [Errors](docs/gitbooks/en/developing/embed/concepts/errors.md): Match the error type at its boundary: `RuntimeError` describes boot/configuration, `AgentError` describes registration/layout, and `CoreError` describes typed facade calls and turns. A missing compiled or selected domain is `CoreError::Unavailable`; it is a capability decision the host can reflect in its UI.\n- [Hooks and seams](docs/gitbooks/en/developing/embed/concepts/hooks-seams.md): Runtime builders accept host-owned ports for storage, sessions, memory, tools and controller extensions. These are composition seams: the host supplies an implementation of the owning library's contract, while OpenHuman retains access policy, approvals and lifecycle.\n- [MCP](docs/gitbooks/en/developing/embed/concepts/mcp.md): MCP connects a native agent turn to tools served over an MCP protocol transport. Configure a server on `AgentSpec::mcp`, with its transport, authentication and allowed tool names. Another agent on the same runtime does not inherit that server configuration.\n- [Memory](docs/gitbooks/en/developing/embed/concepts/memory.md): `Runtime::memory(namespace)` returns a facade bound to that namespace. The caller establishes scope before sending record data; namespace identity is not taken from model arguments or record metadata. Learning and deletion use the selected memory engine contract.\n- [Observability](docs/gitbooks/en/developing/embed/concepts/observability.md): Subscribe with `Runtime::events` before starting work you want to observe. Each event has a sequence number and runtime identity, with an agent ID and per-call turn ID where applicable. Repeated calls on the same durable session receive different turn IDs; the session ID is not the observation ID.\n- [Profiles and SaaS](docs/gitbooks/en/developing/embed/concepts/profiles-saas.md): Ordinary runtime agents share an operator's configuration path and credentials. Use `ProfileRuntime` when one server serves different authenticated users who must not share those resources. Provision a profile, install its credential, and retain a `ProfileHandle` while serving that user's requests.\n- [Providers](docs/gitbooks/en/developing/embed/concepts/providers.md): A runtime provider is the default inference choice for its agents. An agent can override it with an OpenAI-compatible route or a native `ChatModel<()>` through `Provider::custom`. The `providers` facade exposes the request/response contracts so custom adapters do not need to implement an HTTP server.\n- [Runtime defaults](docs/gitbooks/en/developing/embed/concepts/runtime-defaults.md): The runtime's default provider, access policy and `ModelDefaults` reduce repeated setup. `AgentSpec` can override those defaults for an analyst, a writing agent or another workload without changing its siblings. Temperature and token limits are forwarded to the selected native model request, rather than being merely descriptive builder values.\n- [Runtime](docs/gitbooks/en/developing/embed/concepts/runtime.md): A `Runtime` boots the in-process core once. It owns the selected domains, background services, module host and runtime defaults. Its agents share that core while using separately derived contexts. Build one runtime and pass an `Arc<Runtime>` to the parts of your application that register agents.\n- [Skills](docs/gitbooks/en/developing/embed/concepts/skills.md): A skill bundle contains `SKILL.md` plus any referenced resources. `AgentSpec::skills_dir` copies bundles into the agent's `agents/<id>/skills/` tree. Copying is intentional: discovery rejects symlinked bundles, so linking a shared directory does not install it.\n- [Testing](docs/gitbooks/en/developing/embed/concepts/testing.md): The runnable examples default to loopback fixtures. They assert request bodies, tool results, file effects, transcript scope and event ordering; a successful Rust compilation alone does not prove those behaviors. Live example execution is explicitly opt-in through the documented environment variables.\n- [Tools](docs/gitbooks/en/developing/embed/concepts/tools.md): An agent's `ToolScopeSpec` chooses built-ins, named tools or `HostOnly`. Host-only mode starts with the host's advertised tools, requires an explicit bare prompt, and does not inherit an orchestrator tool catalog. This is useful when your application already owns read-only data access or tightly scoped actions.\n- [Turns and sessions](docs/gitbooks/en/developing/embed/concepts/turns-sessions.md): `Agent::run` collects a turn. `Agent::turn` lets you set an explicit session, per-turn options, progress sink and cancellation before sending. Reuse the returned session identity for follow-up messages that should see the same transcript. Two calls on the same durable session remain distinct turn executions and receive distinct observation IDs.\n- [Embedding FAQ](docs/gitbooks/en/developing/embed/faq.md): An ordinary embed process supports one `Runtime`; a second build returns `AlreadyRunning`. Add agents to the existing runtime. A multi-user host uses `ProfileRuntime` instead and needs a process dedicated to that mode. See runtime and profiles.\n- [Embedding guides](docs/gitbooks/en/developing/embed/guides/README.md): Choose the guide that matches your host:\n- [CLI chatbot](docs/gitbooks/en/developing/embed/guides/cli-chatbot.md): Keep one `Runtime` alive for the process and create an `Agent` for the conversation. The executable example starts with a single message, checks the reply, and prints it. A CLI input loop can send subsequent messages through the same agent; choose a stable thread identity when your host exposes separate conversations.\n- [Deploy a server](docs/gitbooks/en/developing/embed/guides/deploy-server.md): Your HTTP host can keep a runtime alive and invoke its agents directly. The executable example binds a loopback listener, handles one request, and sends back the agent's reply. It exercises the request path without requiring an RPC server dependency in `openhuman-embed`.\n- [Lean and headless hosts](docs/gitbooks/en/developing/embed/guides/lean-headless.md): `RuntimeBuilder::lean()` selects the agent, memory, and inference families without background services. Runtime module selection controls what is active; Cargo features control what is compiled. A lean preset alone does not shed compiled dependencies.\n- [Test with a mock backend](docs/gitbooks/en/developing/embed/guides/mock-backend-testing.md): Run `node scripts/run-embed-examples.mjs` from the repository root. The runner builds and executes every example with the union of required features, clears inherited live-example credentials and operator workspace settings, and requires each program's `EXAMPLE_OK` marker after its assertions.\n- [Multiple agents](docs/gitbooks/en/developing/embed/guides/multi-agent.md): A runtime owns shared services and credentials. Each `AgentSpec` selects its prompt, model route, access policy, and `action_dir`. Internal agent state belongs to its agent home; the action directory is where acting tools operate.\n- [SaaS with multiple tenants](docs/gitbooks/en/developing/embed/guides/saas-multi-tenant.md): Use `ProfileRuntime` for a process serving multiple users. Provision each profile, install that profile's credential, and open a handle to its workspace. Reusing a thread id in two profiles does not share their conversation history.\n- [Installation](docs/gitbooks/en/developing/embed/installation.md): `openhuman-embed` is currently an unpublished workspace crate. Use a local path dependency when developing in this repository, or a Git dependency on `https://github.com/tinyhumansai/openhuman` with `package = \"openhuman-embed\"`. Pin a reviewed Git revision for a deployed application; the nested vendored libraries are part of that revision's build inputs.\n- [Embedding integrations](docs/gitbooks/en/developing/embed/integrations/README.md): An embedded runtime composes the capabilities its host supplies. Start with the adapter your application needs:\n- [Channels](docs/gitbooks/en/developing/embed/integrations/channels.md): Enable the `channels` Cargo feature to use channel adapters. Your host configures the channel credential and binds incoming messages to an agent. In the Telegram example, the Bot API endpoint is a loopback stub, so updates and replies exercise the adapter without contacting Telegram.\n- [Composio](docs/gitbooks/en/developing/embed/integrations/composio.md): Composio calls use the host backend transport. Install that transport before booting a managed runtime; see TinyHumans managed inference. An explicit inference provider route supplies model inference, not the connector transport.\n- [Local and custom models](docs/gitbooks/en/developing/embed/integrations/local-models.md): For a local server that implements chat completions, use `Provider::openai_compatible` with its API root and model id. The constructor requires a bearer string even when a particular local server ignores it. Set the endpoint explicitly rather than relying on another user's installed configuration. The OpenAI-compatible example controls can point a live example at that local server.\n- [MCP servers](docs/gitbooks/en/developing/embed/integrations/mcp.md): Enable the `mcp` Cargo feature and declare a server with `AgentSpec::mcp`. `McpServer::http` takes a registered name and endpoint; `allow_tools` narrows the remote tool list. Server configuration and its connections belong to the agent that declared them.\n- [OpenAI-compatible providers and BYOK](docs/gitbooks/en/developing/embed/integrations/openai-compatible.md): Construct a route with `Provider::openai_compatible(base_url, api_key).model(model_id)`, then set it on the runtime or an individual agent. Pass the API root, such as a provider's `/v1` URL; the adapter appends `/chat/completions`. Supply an explicit model id so the route can register that model for its workload roles.\n- [Storage drivers](docs/gitbooks/en/developing/embed/integrations/storage-drivers.md): Enable `storage-sqlite` for SQLite or `storage-mongodb` for MongoDB on your Embed dependency. These Cargo gates make the driver available; choosing a URL selects the runtime backend. Set `RuntimeConfig.storage.url`, pass a URL through `RuntimeBuilder::storage`, or set the `url` field in the `[storage]` section of the host's configuration. `OPENHUMAN_STORAGE_URL` is the environment override used by host configuration resolution.\n- [TinyHumans managed inference](docs/gitbooks/en/developing/embed/integrations/tinyhumans-managed.md): Embed wraps the core; neither contains a TinyHumans backend client. A managed host boots through the builder in `openhuman-tinyhumans` (`openhuman_tinyhumans::RuntimeBuilder`), or calls `openhuman_tinyhumans::install`. That layer owns the transport, the authenticated client, the hosted routes and the hosted providers, and it plugs them into the core through the core's generic registries (chat models, voice, tool sources, the channel callback and memory engines). A host can register its own provider in the same registries. The connected host layer also owns session login. `RuntimeBuilder::backend_url` selects a URL; it does not connect anything.\n- [Web search](docs/gitbooks/en/developing/embed/integrations/web-search.md): An agent's web-search tools (`web_search_tool`, `web_answer_tool`, `web_contents_tool`) run through the process-wide TinySearch module. Several agents in one runtime can each search with their own provider key: pin it on that agent's config, and it is never written to `config.toml`, the environment, or a log.\n- [Quickstart](docs/gitbooks/en/developing/embed/quickstart.md): Start from a checkout of the repository and run `cargo run -p openhuman-embed --example run_turn`. The example boots an ephemeral runtime, creates its own local provider/backend fixtures and prints `hello from the stub`. It needs no inference account and does not contact a real backend.\n- [Embedding troubleshooting](docs/gitbooks/en/developing/embed/troubleshooting.md): Use the typed error and the effective capability report together. A method that was compiled out is a different problem from a configured method with a provider failure. Keep credentials, user prompts and raw provider payloads out of routine diagnostics.\n- [OpenHuman Embed](crates/openhuman-embed/README.md): A typed Rust facade for running the OpenHuman core inside your application: one Runtime per process, with independently configured Agents.\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embedding.md): The embedding documentation now lives in the Embedding section.\n- [Cookbook](docs/gitbooks/en/developing/embed/cookbook.md): Executable recipes are indexed from their source headers. Each entry records the declared run mode and any named Embed feature required by the example. Full source includes the surrounding setup and assertions.\n- [Compiled feature matrix](docs/gitbooks/en/developing/embed/capability-matrix.md): This matrix comes from the default-feature compiled capability-report example. Runtime presets can narrow enabled domains and services; core Cargo features determine which implementations are available. Named Embed features also gate facade exports: enable `mcp` or `skills` explicitly for their public setters even when core defaults compile those implementations. Embed defaults enable the named `channels` feature. See the installation guide for these separate layers.\n- [RuntimeBuilder setters](docs/gitbooks/en/developing/embed/builder-setters.md): Public consuming methods that return `Self`, extracted from the RuntimeBuilder implementations. Static host and weight presets, inspection methods and build/run methods are excluded.\n- [API index](docs/gitbooks/en/developing/embed/api-index.md): Generate the complete local Rust API reference with `cargo doc -p openhuman-embed --all-features --no-deps --open`. This crate is currently unpublished; docs.rs is a future publication target. Names below are source-listed exports, including conditional Cargo-gated items; local rustdoc with your selected features describes availability. Canonical facade source.\n- [Embed examples](crates/openhuman-embed/examples/README.md): Cookbook\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/plugin.json)"},"files":{"AGENTS.md":"# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r","CLAUDE.md":"# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r","llms.txt":"# OpenHuman Embed\n\n> Embed OpenHuman runtimes and independently configured agents in Rust applications.\n\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embed/README.md): `openhuman-embed` runs the OpenHuman core inside your Rust program. Your host owns the user interface, server transport and authentication flow; the core owns agent turns, memory, tools, approvals and execution policy. Calling its typed facades does not require a JSON-RPC server or the desktop shell.\n- [Embedding API reference](docs/gitbooks/en/developing/embed/api-reference.md): Generate usable local Rust API documentation with `cargo doc -p openhuman-embed --no-deps --open`. Include `--features mcp,skills,channels,storage-sqlite` for the facade gates used by the offline examples, or select the feature set your host actually builds. For all exported optional surfaces use `--all-features`.\n- [Embedding architecture](docs/gitbooks/en/developing/embed/architecture.md): Embed is the library facade above `openhuman-core`. The TinyHumans layer installs the backend SDK transport and owns product login/session behavior; the RPC layer adds JSON-RPC, server listeners and shipped host boot. A Rust embedder can stay at the facade when it owns those concerns itself.\n- [Concepts](docs/gitbooks/en/developing/embed/concepts/README.md): Start with Runtime and agents to understand which settings are shared. Then follow the lifecycle of a request through provider selection, tools, approvals and its durable session. The scope tables distinguish runtime composition from the state a host must isolate per agent or profile.\n- [Access and approvals](docs/gitbooks/en/developing/embed/concepts/access-approvals.md): `Access` groups permission level, origin, standing grants and trusted roots. Choose an origin and tier together: a trusted local host request and an external channel message are different security contexts. `Access::full()` configures both access fields. A sandbox is an additional execution boundary, configured through `AgentDefinitionSpec`.\n- [Agents](docs/gitbooks/en/developing/embed/concepts/agents.md): Register an `AgentSpec` with `Runtime::agent`. Its ID selects the agent's home, durable session identity and addressed cron/channel work. An `Agent` clone refers to the same instance; it does not copy state or start another core. Runtime-wide credentials remain shared, so two agents are not two authenticated customers.\n- [Channels](docs/gitbooks/en/developing/embed/concepts/channels.md): `Runtime::channels` creates listeners addressed to a live agent ID. A listener owns its transport task; dropping or stopping it ends that listener. The agent supplies the prompt, provider and tools used for every incoming message instead of handing messages to an operator agent.\n- [Completer](docs/gitbooks/en/developing/embed/concepts/completer.md): Use `Completer` for one-shot text or structured generation when you do not need an agent, its tool loop or persistent transcript. It takes an explicit route and a `CompletionRequest`; the caller supplies the model and messages for every request.\n- [Cron](docs/gitbooks/en/developing/embed/concepts/cron.md): The typed `Cron` facade creates named jobs with `JobSpec` and `JobSchedule`. `upsert` preserves a job's identity when the same name is configured again; `runs` exposes execution history. An agent-targeted job resolves the configured agent ID and executes with that agent's provider, prompt, tools and derived context.\n- [Errors](docs/gitbooks/en/developing/embed/concepts/errors.md): Match the error type at its boundary: `RuntimeError` describes boot/configuration, `AgentError` describes registration/layout, and `CoreError` describes typed facade calls and turns. A missing compiled or selected domain is `CoreError::Unavailable`; it is a capability decision the host can reflect in its UI.\n- [Hooks and seams](docs/gitbooks/en/developing/embed/concepts/hooks-seams.md): Runtime builders accept host-owned ports for storage, sessions, memory, tools and controller extensions. These are composition seams: the host supplies an implementation of the owning library's contract, while OpenHuman retains access policy, approvals and lifecycle.\n- [MCP](docs/gitbooks/en/developing/embed/concepts/mcp.md): MCP connects a native agent turn to tools served over an MCP protocol transport. Configure a server on `AgentSpec::mcp`, with its transport, authentication and allowed tool names. Another agent on the same runtime does not inherit that server configuration.\n- [Memory](docs/gitbooks/en/developing/embed/concepts/memory.md): `Runtime::memory(namespace)` returns a facade bound to that namespace. The caller establishes scope before sending record data; namespace identity is not taken from model arguments or record metadata. Learning and deletion use the selected memory engine contract.\n- [Observability](docs/gitbooks/en/developing/embed/concepts/observability.md): Subscribe with `Runtime::events` before starting work you want to observe. Each event has a sequence number and runtime identity, with an agent ID and per-call turn ID where applicable. Repeated calls on the same durable session receive different turn IDs; the session ID is not the observation ID.\n- [Profiles and SaaS](docs/gitbooks/en/developing/embed/concepts/profiles-saas.md): Ordinary runtime agents share an operator's configuration path and credentials. Use `ProfileRuntime` when one server serves different authenticated users who must not share those resources. Provision a profile, install its credential, and retain a `ProfileHandle` while serving that user's requests.\n- [Providers](docs/gitbooks/en/developing/embed/concepts/providers.md): A runtime provider is the default inference choice for its agents. An agent can override it with an OpenAI-compatible route or a native `ChatModel<()>` through `Provider::custom`. The `providers` facade exposes the request/response contracts so custom adapters do not need to implement an HTTP server.\n- [Runtime defaults](docs/gitbooks/en/developing/embed/concepts/runtime-defaults.md): The runtime's default provider, access policy and `ModelDefaults` reduce repeated setup. `AgentSpec` can override those defaults for an analyst, a writing agent or another workload without changing its siblings. Temperature and token limits are forwarded to the selected native model request, rather than being merely descriptive builder values.\n- [Runtime](docs/gitbooks/en/developing/embed/concepts/runtime.md): A `Runtime` boots the in-process core once. It owns the selected domains, background services, module host and runtime defaults. Its agents share that core while using separately derived contexts. Build one runtime and pass an `Arc<Runtime>` to the parts of your application that register agents.\n- [Skills](docs/gitbooks/en/developing/embed/concepts/skills.md): A skill bundle contains `SKILL.md` plus any referenced resources. `AgentSpec::skills_dir` copies bundles into the agent's `agents/<id>/skills/` tree. Copying is intentional: discovery rejects symlinked bundles, so linking a shared directory does not install it.\n- [Testing](docs/gitbooks/en/developing/embed/concepts/testing.md): The runnable examples default to loopback fixtures. They assert request bodies, tool results, file effects, transcript scope and event ordering; a successful Rust compilation alone does not prove those behaviors. Live example execution is explicitly opt-in through the documented environment variables.\n- [Tools](docs/gitbooks/en/developing/embed/concepts/tools.md): An agent's `ToolScopeSpec` chooses built-ins, named tools or `HostOnly`. Host-only mode starts with the host's advertised tools, requires an explicit bare prompt, and does not inherit an orchestrator tool catalog. This is useful when your application already owns read-only data access or tightly scoped actions.\n- [Turns and sessions](docs/gitbooks/en/developing/embed/concepts/turns-sessions.md): `Agent::run` collects a turn. `Agent::turn` lets you set an explicit session, per-turn options, progress sink and cancellation before sending. Reuse the returned session identity for follow-up messages that should see the same transcript. Two calls on the same durable session remain distinct turn executions and receive distinct observation IDs.\n- [Embedding FAQ](docs/gitbooks/en/developing/embed/faq.md): An ordinary embed process supports one `Runtime`; a second build returns `AlreadyRunning`. Add agents to the existing runtime. A multi-user host uses `ProfileRuntime` instead and needs a process dedicated to that mode. See runtime and profiles.\n- [Embedding guides](docs/gitbooks/en/developing/embed/guides/README.md): Choose the guide that matches your host:\n- [CLI chatbot](docs/gitbooks/en/developing/embed/guides/cli-chatbot.md): Keep one `Runtime` alive for the process and create an `Agent` for the conversation. The executable example starts with a single message, checks the reply, and prints it. A CLI input loop can send subsequent messages through the same agent; choose a stable thread identity when your host exposes separate conversations.\n- [Deploy a server](docs/gitbooks/en/developing/embed/guides/deploy-server.md): Your HTTP host can keep a runtime alive and invoke its agents directly. The executable example binds a loopback listener, handles one request, and sends back the agent's reply. It exercises the request path without requiring an RPC server dependency in `openhuman-embed`.\n- [Lean and headless hosts](docs/gitbooks/en/developing/embed/guides/lean-headless.md): `RuntimeBuilder::lean()` selects the agent, memory, and inference families without background services. Runtime module selection controls what is active; Cargo features control what is compiled. A lean preset alone does not shed compiled dependencies.\n- [Test with a mock backend](docs/gitbooks/en/developing/embed/guides/mock-backend-testing.md): Run `node scripts/run-embed-examples.mjs` from the repository root. The runner builds and executes every example with the union of required features, clears inherited live-example credentials and operator workspace settings, and requires each program's `EXAMPLE_OK` marker after its assertions.\n- [Multiple agents](docs/gitbooks/en/developing/embed/guides/multi-agent.md): A runtime owns shared services and credentials. Each `AgentSpec` selects its prompt, model route, access policy, and `action_dir`. Internal agent state belongs to its agent home; the action directory is where acting tools operate.\n- [SaaS with multiple tenants](docs/gitbooks/en/developing/embed/guides/saas-multi-tenant.md): Use `ProfileRuntime` for a process serving multiple users. Provision each profile, install that profile's credential, and open a handle to its workspace. Reusing a thread id in two profiles does not share their conversation history.\n- [Installation](docs/gitbooks/en/developing/embed/installation.md): `openhuman-embed` is currently an unpublished workspace crate. Use a local path dependency when developing in this repository, or a Git dependency on `https://github.com/tinyhumansai/openhuman` with `package = \"openhuman-embed\"`. Pin a reviewed Git revision for a deployed application; the nested vendored libraries are part of that revision's build inputs.\n- [Embedding integrations](docs/gitbooks/en/developing/embed/integrations/README.md): An embedded runtime composes the capabilities its host supplies. Start with the adapter your application needs:\n- [Channels](docs/gitbooks/en/developing/embed/integrations/channels.md): Enable the `channels` Cargo feature to use channel adapters. Your host configures the channel credential and binds incoming messages to an agent. In the Telegram example, the Bot API endpoint is a loopback stub, so updates and replies exercise the adapter without contacting Telegram.\n- [Composio](docs/gitbooks/en/developing/embed/integrations/composio.md): Composio calls use the host backend transport. Install that transport before booting a managed runtime; see TinyHumans managed inference. An explicit inference provider route supplies model inference, not the connector transport.\n- [Local and custom models](docs/gitbooks/en/developing/embed/integrations/local-models.md): For a local server that implements chat completions, use `Provider::openai_compatible` with its API root and model id. The constructor requires a bearer string even when a particular local server ignores it. Set the endpoint explicitly rather than relying on another user's installed configuration. The OpenAI-compatible example controls can point a live example at that local server.\n- [MCP servers](docs/gitbooks/en/developing/embed/integrations/mcp.md): Enable the `mcp` Cargo feature and declare a server with `AgentSpec::mcp`. `McpServer::http` takes a registered name and endpoint; `allow_tools` narrows the remote tool list. Server configuration and its connections belong to the agent that declared them.\n- [OpenAI-compatible providers and BYOK](docs/gitbooks/en/developing/embed/integrations/openai-compatible.md): Construct a route with `Provider::openai_compatible(base_url, api_key).model(model_id)`, then set it on the runtime or an individual agent. Pass the API root, such as a provider's `/v1` URL; the adapter appends `/chat/completions`. Supply an explicit model id so the route can register that model for its workload roles.\n- [Storage drivers](docs/gitbooks/en/developing/embed/integrations/storage-drivers.md): Enable `storage-sqlite` for SQLite or `storage-mongodb` for MongoDB on your Embed dependency. These Cargo gates make the driver available; choosing a URL selects the runtime backend. Set `RuntimeConfig.storage.url`, pass a URL through `RuntimeBuilder::storage`, or set the `url` field in the `[storage]` section of the host's configuration. `OPENHUMAN_STORAGE_URL` is the environment override used by host configuration resolution.\n- [TinyHumans managed inference](docs/gitbooks/en/developing/embed/integrations/tinyhumans-managed.md): Embed wraps the core; neither contains a TinyHumans backend client. A managed host boots through the builder in `openhuman-tinyhumans` (`openhuman_tinyhumans::RuntimeBuilder`), or calls `openhuman_tinyhumans::install`. That layer owns the transport, the authenticated client, the hosted routes and the hosted providers, and it plugs them into the core through the core's generic registries (chat models, voice, tool sources, the channel callback and memory engines). A host can register its own provider in the same registries. The connected host layer also owns session login. `RuntimeBuilder::backend_url` selects a URL; it does not connect anything.\n- [Web search](docs/gitbooks/en/developing/embed/integrations/web-search.md): An agent's web-search tools (`web_search_tool`, `web_answer_tool`, `web_contents_tool`) run through the process-wide TinySearch module. Several agents in one runtime can each search with their own provider key: pin it on that agent's config, and it is never written to `config.toml`, the environment, or a log.\n- [Quickstart](docs/gitbooks/en/developing/embed/quickstart.md): Start from a checkout of the repository and run `cargo run -p openhuman-embed --example run_turn`. The example boots an ephemeral runtime, creates its own local provider/backend fixtures and prints `hello from the stub`. It needs no inference account and does not contact a real backend.\n- [Embedding troubleshooting](docs/gitbooks/en/developing/embed/troubleshooting.md): Use the typed error and the effective capability report together. A method that was compiled out is a different problem from a configured method with a provider failure. Keep credentials, user prompts and raw provider payloads out of routine diagnostics.\n- [OpenHuman Embed](crates/openhuman-embed/README.md): A typed Rust facade for running the OpenHuman core inside your application: one Runtime per process, with independently configured Agents.\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embedding.md): The embedding documentation now lives in the Embedding section.\n- [Cookbook](docs/gitbooks/en/developing/embed/cookbook.md): Executable recipes are indexed from their source headers. Each entry records the declared run mode and any named Embed feature required by the example. Full source includes the surrounding setup and assertions.\n- [Compiled feature matrix](docs/gitbooks/en/developing/embed/capability-matrix.md): This matrix comes from the default-feature compiled capability-report example. Runtime presets can narrow enabled domains and services; core Cargo features determine which implementations are available. Named Embed features also gate facade exports: enable `mcp` or `skills` explicitly for their public setters even when core defaults compile those implementations. Embed defaults enable the named `channels` feature. See the installation guide for these separate layers.\n- [RuntimeBuilder setters](docs/gitbooks/en/developing/embed/builder-setters.md): Public consuming methods that return `Self`, extracted from the RuntimeBuilder implementations. Static host and weight presets, inspection methods and build/run methods are excluded.\n- [API index](docs/gitbooks/en/developing/embed/api-index.md): Generate the complete local Rust API reference with `cargo doc -p openhuman-embed --all-features --no-deps --open`. This crate is currently unpublished; docs.rs is a future publication target. Names below are source-listed exports, including conditional Cargo-gated items; local rustdoc with your selected features describes availability. Canonical facade source.\n- [Embed examples](crates/openhuman-embed/examples/README.md): Cookbook\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/plugin.json)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r","isInternal":false,"tokens":12170,"sizeBytes":50012},{"name":"llms.txt","path":"llms.txt","rawUrl":"https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/llms.txt","title":"LLM Index & Context Digest","category":"root-instruction","format":"text","content":"# OpenHuman Embed\n\n> Embed OpenHuman runtimes and independently configured agents in Rust applications.\n\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embed/README.md): `openhuman-embed` runs the OpenHuman core inside your Rust program. Your host owns the user interface, server transport and authentication flow; the core owns agent turns, memory, tools, approvals and execution policy. Calling its typed facades does not require a JSON-RPC server or the desktop shell.\n- [Embedding API reference](docs/gitbooks/en/developing/embed/api-reference.md): Generate usable local Rust API documentation with `cargo doc -p openhuman-embed --no-deps --open`. Include `--features mcp,skills,channels,storage-sqlite` for the facade gates used by the offline examples, or select the feature set your host actually builds. For all exported optional surfaces use `--all-features`.\n- [Embedding architecture](docs/gitbooks/en/developing/embed/architecture.md): Embed is the library facade above `openhuman-core`. The TinyHumans layer installs the backend SDK transport and owns product login/session behavior; the RPC layer adds JSON-RPC, server listeners and shipped host boot. A Rust embedder can stay at the facade when it owns those concerns itself.\n- [Concepts](docs/gitbooks/en/developing/embed/concepts/README.md): Start with Runtime and agents to understand which settings are shared. Then follow the lifecycle of a request through provider selection, tools, approvals and its durable session. The scope tables distinguish runtime composition from the state a host must isolate per agent or profile.\n- [Access and approvals](docs/gitbooks/en/developing/embed/concepts/access-approvals.md): `Access` groups permission level, origin, standing grants and trusted roots. Choose an origin and tier together: a trusted local host request and an external channel message are different security contexts. `Access::full()` configures both access fields. A sandbox is an additional execution boundary, configured through `AgentDefinitionSpec`.\n- [Agents](docs/gitbooks/en/developing/embed/concepts/agents.md): Register an `AgentSpec` with `Runtime::agent`. Its ID selects the agent's home, durable session identity and addressed cron/channel work. An `Agent` clone refers to the same instance; it does not copy state or start another core. Runtime-wide credentials remain shared, so two agents are not two authenticated customers.\n- [Channels](docs/gitbooks/en/developing/embed/concepts/channels.md): `Runtime::channels` creates listeners addressed to a live agent ID. A listener owns its transport task; dropping or stopping it ends that listener. The agent supplies the prompt, provider and tools used for every incoming message instead of handing messages to an operator agent.\n- [Completer](docs/gitbooks/en/developing/embed/concepts/completer.md): Use `Completer` for one-shot text or structured generation when you do not need an agent, its tool loop or persistent transcript. It takes an explicit route and a `CompletionRequest`; the caller supplies the model and messages for every request.\n- [Cron](docs/gitbooks/en/developing/embed/concepts/cron.md): The typed `Cron` facade creates named jobs with `JobSpec` and `JobSchedule`. `upsert` preserves a job's identity when the same name is configured again; `runs` exposes execution history. An agent-targeted job resolves the configured agent ID and executes with that agent's provider, prompt, tools and derived context.\n- [Errors](docs/gitbooks/en/developing/embed/concepts/errors.md): Match the error type at its boundary: `RuntimeError` describes boot/configuration, `AgentError` describes registration/layout, and `CoreError` describes typed facade calls and turns. A missing compiled or selected domain is `CoreError::Unavailable`; it is a capability decision the host can reflect in its UI.\n- [Hooks and seams](docs/gitbooks/en/developing/embed/concepts/hooks-seams.md): Runtime builders accept host-owned ports for storage, sessions, memory, tools and controller extensions. These are composition seams: the host supplies an implementation of the owning library's contract, while OpenHuman retains access policy, approvals and lifecycle.\n- [MCP](docs/gitbooks/en/developing/embed/concepts/mcp.md): MCP connects a native agent turn to tools served over an MCP protocol transport. Configure a server on `AgentSpec::mcp`, with its transport, authentication and allowed tool names. Another agent on the same runtime does not inherit that server configuration.\n- [Memory](docs/gitbooks/en/developing/embed/concepts/memory.md): `Runtime::memory(namespace)` returns a facade bound to that namespace. The caller establishes scope before sending record data; namespace identity is not taken from model arguments or record metadata. Learning and deletion use the selected memory engine contract.\n- [Observability](docs/gitbooks/en/developing/embed/concepts/observability.md): Subscribe with `Runtime::events` before starting work you want to observe. Each event has a sequence number and runtime identity, with an agent ID and per-call turn ID where applicable. Repeated calls on the same durable session receive different turn IDs; the session ID is not the observation ID.\n- [Profiles and SaaS](docs/gitbooks/en/developing/embed/concepts/profiles-saas.md): Ordinary runtime agents share an operator's configuration path and credentials. Use `ProfileRuntime` when one server serves different authenticated users who must not share those resources. Provision a profile, install its credential, and retain a `ProfileHandle` while serving that user's requests.\n- [Providers](docs/gitbooks/en/developing/embed/concepts/providers.md): A runtime provider is the default inference choice for its agents. An agent can override it with an OpenAI-compatible route or a native `ChatModel<()>` through `Provider::custom`. The `providers` facade exposes the request/response contracts so custom adapters do not need to implement an HTTP server.\n- [Runtime defaults](docs/gitbooks/en/developing/embed/concepts/runtime-defaults.md): The runtime's default provider, access policy and `ModelDefaults` reduce repeated setup. `AgentSpec` can override those defaults for an analyst, a writing agent or another workload without changing its siblings. Temperature and token limits are forwarded to the selected native model request, rather than being merely descriptive builder values.\n- [Runtime](docs/gitbooks/en/developing/embed/concepts/runtime.md): A `Runtime` boots the in-process core once. It owns the selected domains, background services, module host and runtime defaults. Its agents share that core while using separately derived contexts. Build one runtime and pass an `Arc<Runtime>` to the parts of your application that register agents.\n- [Skills](docs/gitbooks/en/developing/embed/concepts/skills.md): A skill bundle contains `SKILL.md` plus any referenced resources. `AgentSpec::skills_dir` copies bundles into the agent's `agents/<id>/skills/` tree. Copying is intentional: discovery rejects symlinked bundles, so linking a shared directory does not install it.\n- [Testing](docs/gitbooks/en/developing/embed/concepts/testing.md): The runnable examples default to loopback fixtures. They assert request bodies, tool results, file effects, transcript scope and event ordering; a successful Rust compilation alone does not prove those behaviors. Live example execution is explicitly opt-in through the documented environment variables.\n- [Tools](docs/gitbooks/en/developing/embed/concepts/tools.md): An agent's `ToolScopeSpec` chooses built-ins, named tools or `HostOnly`. Host-only mode starts with the host's advertised tools, requires an explicit bare prompt, and does not inherit an orchestrator tool catalog. This is useful when your application already owns read-only data access or tightly scoped actions.\n- [Turns and sessions](docs/gitbooks/en/developing/embed/concepts/turns-sessions.md): `Agent::run` collects a turn. `Agent::turn` lets you set an explicit session, per-turn options, progress sink and cancellation before sending. Reuse the returned session identity for follow-up messages that should see the same transcript. Two calls on the same durable session remain distinct turn executions and receive distinct observation IDs.\n- [Embedding FAQ](docs/gitbooks/en/developing/embed/faq.md): An ordinary embed process supports one `Runtime`; a second build returns `AlreadyRunning`. Add agents to the existing runtime. A multi-user host uses `ProfileRuntime` instead and needs a process dedicated to that mode. See runtime and profiles.\n- [Embedding guides](docs/gitbooks/en/developing/embed/guides/README.md): Choose the guide that matches your host:\n- [CLI chatbot](docs/gitbooks/en/developing/embed/guides/cli-chatbot.md): Keep one `Runtime` alive for the process and create an `Agent` for the conversation. The executable example starts with a single message, checks the reply, and prints it. A CLI input loop can send subsequent messages through the same agent; choose a stable thread identity when your host exposes separate conversations.\n- [Deploy a server](docs/gitbooks/en/developing/embed/guides/deploy-server.md): Your HTTP host can keep a runtime alive and invoke its agents directly. The executable example binds a loopback listener, handles one request, and sends back the agent's reply. It exercises the request path without requiring an RPC server dependency in `openhuman-embed`.\n- [Lean and headless hosts](docs/gitbooks/en/developing/embed/guides/lean-headless.md): `RuntimeBuilder::lean()` selects the agent, memory, and inference families without background services. Runtime module selection controls what is active; Cargo features control what is compiled. A lean preset alone does not shed compiled dependencies.\n- [Test with a mock backend](docs/gitbooks/en/developing/embed/guides/mock-backend-testing.md): Run `node scripts/run-embed-examples.mjs` from the repository root. The runner builds and executes every example with the union of required features, clears inherited live-example credentials and operator workspace settings, and requires each program's `EXAMPLE_OK` marker after its assertions.\n- [Multiple agents](docs/gitbooks/en/developing/embed/guides/multi-agent.md): A runtime owns shared services and credentials. Each `AgentSpec` selects its prompt, model route, access policy, and `action_dir`. Internal agent state belongs to its agent home; the action directory is where acting tools operate.\n- [SaaS with multiple tenants](docs/gitbooks/en/developing/embed/guides/saas-multi-tenant.md): Use `ProfileRuntime` for a process serving multiple users. Provision each profile, install that profile's credential, and open a handle to its workspace. Reusing a thread id in two profiles does not share their conversation history.\n- [Installation](docs/gitbooks/en/developing/embed/installation.md): `openhuman-embed` is currently an unpublished workspace crate. Use a local path dependency when developing in this repository, or a Git dependency on `https://github.com/tinyhumansai/openhuman` with `package = \"openhuman-embed\"`. Pin a reviewed Git revision for a deployed application; the nested vendored libraries are part of that revision's build inputs.\n- [Embedding integrations](docs/gitbooks/en/developing/embed/integrations/README.md): An embedded runtime composes the capabilities its host supplies. Start with the adapter your application needs:\n- [Channels](docs/gitbooks/en/developing/embed/integrations/channels.md): Enable the `channels` Cargo feature to use channel adapters. Your host configures the channel credential and binds incoming messages to an agent. In the Telegram example, the Bot API endpoint is a loopback stub, so updates and replies exercise the adapter without contacting Telegram.\n- [Composio](docs/gitbooks/en/developing/embed/integrations/composio.md): Composio calls use the host backend transport. Install that transport before booting a managed runtime; see TinyHumans managed inference. An explicit inference provider route supplies model inference, not the connector transport.\n- [Local and custom models](docs/gitbooks/en/developing/embed/integrations/local-models.md): For a local server that implements chat completions, use `Provider::openai_compatible` with its API root and model id. The constructor requires a bearer string even when a particular local server ignores it. Set the endpoint explicitly rather than relying on another user's installed configuration. The OpenAI-compatible example controls can point a live example at that local server.\n- [MCP servers](docs/gitbooks/en/developing/embed/integrations/mcp.md): Enable the `mcp` Cargo feature and declare a server with `AgentSpec::mcp`. `McpServer::http` takes a registered name and endpoint; `allow_tools` narrows the remote tool list. Server configuration and its connections belong to the agent that declared them.\n- [OpenAI-compatible providers and BYOK](docs/gitbooks/en/developing/embed/integrations/openai-compatible.md): Construct a route with `Provider::openai_compatible(base_url, api_key).model(model_id)`, then set it on the runtime or an individual agent. Pass the API root, such as a provider's `/v1` URL; the adapter appends `/chat/completions`. Supply an explicit model id so the route can register that model for its workload roles.\n- [Storage drivers](docs/gitbooks/en/developing/embed/integrations/storage-drivers.md): Enable `storage-sqlite` for SQLite or `storage-mongodb` for MongoDB on your Embed dependency. These Cargo gates make the driver available; choosing a URL selects the runtime backend. Set `RuntimeConfig.storage.url`, pass a URL through `RuntimeBuilder::storage`, or set the `url` field in the `[storage]` section of the host's configuration. `OPENHUMAN_STORAGE_URL` is the environment override used by host configuration resolution.\n- [TinyHumans managed inference](docs/gitbooks/en/developing/embed/integrations/tinyhumans-managed.md): Embed wraps the core; neither contains a TinyHumans backend client. A managed host boots through the builder in `openhuman-tinyhumans` (`openhuman_tinyhumans::RuntimeBuilder`), or calls `openhuman_tinyhumans::install`. That layer owns the transport, the authenticated client, the hosted routes and the hosted providers, and it plugs them into the core through the core's generic registries (chat models, voice, tool sources, the channel callback and memory engines). A host can register its own provider in the same registries. The connected host layer also owns session login. `RuntimeBuilder::backend_url` selects a URL; it does not connect anything.\n- [Web search](docs/gitbooks/en/developing/embed/integrations/web-search.md): An agent's web-search tools (`web_search_tool`, `web_answer_tool`, `web_contents_tool`) run through the process-wide TinySearch module. Several agents in one runtime can each search with their own provider key: pin it on that agent's config, and it is never written to `config.toml`, the environment, or a log.\n- [Quickstart](docs/gitbooks/en/developing/embed/quickstart.md): Start from a checkout of the repository and run `cargo run -p openhuman-embed --example run_turn`. The example boots an ephemeral runtime, creates its own local provider/backend fixtures and prints `hello from the stub`. It needs no inference account and does not contact a real backend.\n- [Embedding troubleshooting](docs/gitbooks/en/developing/embed/troubleshooting.md): Use the typed error and the effective capability report together. A method that was compiled out is a different problem from a configured method with a provider failure. Keep credentials, user prompts and raw provider payloads out of routine diagnostics.\n- [OpenHuman Embed](crates/openhuman-embed/README.md): A typed Rust facade for running the OpenHuman core inside your application: one Runtime per process, with independently configured Agents.\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embedding.md): The embedding documentation now lives in the Embedding section.\n- [Cookbook](docs/gitbooks/en/developing/embed/cookbook.md): Executable recipes are indexed from their source headers. Each entry records the declared run mode and any named Embed feature required by the example. Full source includes the surrounding setup and assertions.\n- [Compiled feature matrix](docs/gitbooks/en/developing/embed/capability-matrix.md): This matrix comes from the default-feature compiled capability-report example. Runtime presets can narrow enabled domains and services; core Cargo features determine which implementations are available. Named Embed features also gate facade exports: enable `mcp` or `skills` explicitly for their public setters even when core defaults compile those implementations. Embed defaults enable the named `channels` feature. See the installation guide for these separate layers.\n- [RuntimeBuilder setters](docs/gitbooks/en/developing/embed/builder-setters.md): Public consuming methods that return `Self`, extracted from the RuntimeBuilder implementations. Static host and weight presets, inspection methods and build/run methods are excluded.\n- [API index](docs/gitbooks/en/developing/embed/api-index.md): Generate the complete local Rust API reference with `cargo doc -p openhuman-embed --all-features --no-deps --open`. This crate is currently unpublished; docs.rs is a future publication target. Names below are source-listed exports, including conditional Cargo-gated items; local rustdoc with your selected features describes availability. Canonical facade source.\n- [Embed examples](crates/openhuman-embed/examples/README.md): Cookbook\n","isInternal":false,"tokens":3676,"sizeBytes":17622},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r","isInternal":false,"tokens":12170,"sizeBytes":50012},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/marketplace.json","title":"Claude Plugin Marketplace Catalog","category":"marketplace","format":"json","content":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/marketplace.json)","tokens":300,"sizeBytes":201},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/plugin.json","title":"claude-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/plugin.json)","tokens":300,"sizeBytes":187},{"name":"mcp.json","path":".cursor/mcp.json","rawUrl":"https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.cursor/mcp.json","title":"Model Context Protocol (MCP) Configuration","category":"mcp-config","format":"json","content":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.cursor/mcp.json)","tokens":300,"sizeBytes":180}],"systemPromptSnippet":"<yakaai_skills repo=\"tinyhumansai/openhuman\">\n<!-- File: AGENTS.md (Tokens: ~12170 | Category: root-instruction) -->\n# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r\n\n<!-- File: llms.txt (Tokens: ~3676 | Category: root-instruction) -->\n# OpenHuman Embed\n\n> Embed OpenHuman runtimes and independently configured agents in Rust applications.\n\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embed/README.md): `openhuman-embed` runs the OpenHuman core inside your Rust program. Your host owns the user interface, server transport and authentication flow; the core owns agent turns, memory, tools, approvals and execution policy. Calling its typed facades does not require a JSON-RPC server or the desktop shell.\n- [Embedding API reference](docs/gitbooks/en/developing/embed/api-reference.md): Generate usable local Rust API documentation with `cargo doc -p openhuman-embed --no-deps --open`. Include `--features mcp,skills,channels,storage-sqlite` for the facade gates used by the offline examples, or select the feature set your host actually builds. For all exported optional surfaces use `--all-features`.\n- [Embedding architecture](docs/gitbooks/en/developing/embed/architecture.md): Embed is the library facade above `openhuman-core`. The TinyHumans layer installs the backend SDK transport and owns product login/session behavior; the RPC layer adds JSON-RPC, server listeners and shipped host boot. A Rust embedder can stay at the facade when it owns those concerns itself.\n- [Concepts](docs/gitbooks/en/developing/embed/concepts/README.md): Start with Runtime and agents to understand which settings are shared. Then follow the lifecycle of a request through provider selection, tools, approvals and its durable session. The scope tables distinguish runtime composition from the state a host must isolate per agent or profile.\n- [Access and approvals](docs/gitbooks/en/developing/embed/concepts/access-approvals.md): `Access` groups permission level, origin, standing grants and trusted roots. Choose an origin and tier together: a trusted local host request and an external channel message are different security contexts. `Access::full()` configures both access fields. A sandbox is an additional execution boundary, configured through `AgentDefinitionSpec`.\n- [Agents](docs/gitbooks/en/developing/embed/concepts/agents.md): Register an `AgentSpec` with `Runtime::agent`. Its ID selects the agent's home, durable session identity and addressed cron/channel work. An `Agent` clone refers to the same instance; it does not copy state or start another core. Runtime-wide credentials remain shared, so two agents are not two authenticated customers.\n- [Channels](docs/gitbooks/en/developing/embed/concepts/channels.md): `Runtime::channels` creates listeners addressed to a live agent ID. A listener owns its transport task; dropping or stopping it ends that listener. The agent supplies the prompt, provider and tools used for every incoming message instead of handing messages to an operator agent.\n- [Completer](docs/gitbooks/en/developing/embed/concepts/completer.md): Use `Completer` for one-shot text or structured generation when you do not need an agent, its tool loop or persistent transcript. It takes an explicit route and a `CompletionRequest`; the caller supplies the model and messages for every request.\n- [Cron](docs/gitbooks/en/developing/embed/concepts/cron.md): The typed `Cron` facade creates named jobs with `JobSpec` and `JobSchedule`. `upsert` preserves a job's identity when the same name is configured again; `runs` exposes execution history. An agent-targeted job resolves the configured agent ID and executes with that agent's provider, prompt, tools and derived context.\n- [Errors](docs/gitbooks/en/developing/embed/concepts/errors.md): Match the error type at its boundary: `RuntimeError` describes boot/configuration, `AgentError` describes registration/layout, and `CoreError` describes typed facade calls and turns. A missing compiled or selected domain is `CoreError::Unavailable`; it is a capability decision the host can reflect in its UI.\n- [Hooks and seams](docs/gitbooks/en/developing/embed/concepts/hooks-seams.md): Runtime builders accept host-owned ports for storage, sessions, memory, tools and controller extensions. These are composition seams: the host supplies an implementation of the owning library's contract, while OpenHuman retains access policy, approvals and lifecycle.\n- [MCP](docs/gitbooks/en/developing/embed/concepts/mcp.md): MCP connects a native agent turn to tools served over an MCP protocol transport. Configure a server on `AgentSpec::mcp`, with its transport, authentication and allowed tool names. Another agent on the same runtime does not inherit that server configuration.\n- [Memory](docs/gitbooks/en/developing/embed/concepts/memory.md): `Runtime::memory(namespace)` returns a facade bound to that namespace. The caller establishes scope before sending record data; namespace identity is not taken from model arguments or record metadata. Learning and deletion use the selected memory engine contract.\n- [Observability](docs/gitbooks/en/developing/embed/concepts/observability.md): Subscribe with `Runtime::events` before starting work you want to observe. Each event has a sequence number and runtime identity, with an agent ID and per-call turn ID where applicable. Repeated calls on the same durable session receive different turn IDs; the session ID is not the observation ID.\n- [Profiles and SaaS](docs/gitbooks/en/developing/embed/concepts/profiles-saas.md): Ordinary runtime agents share an operator's configuration path and credentials. Use `ProfileRuntime` when one server serves different authenticated users who must not share those resources. Provision a profile, install its credential, and retain a `ProfileHandle` while serving that user's requests.\n- [Providers](docs/gitbooks/en/developing/embed/concepts/providers.md): A runtime provider is the default inference choice for its agents. An agent can override it with an OpenAI-compatible route or a native `ChatModel<()>` through `Provider::custom`. The `providers` facade exposes the request/response contracts so custom adapters do not need to implement an HTTP server.\n- [Runtime defaults](docs/gitbooks/en/developing/embed/concepts/runtime-defaults.md): The runtime's default provider, access policy and `ModelDefaults` reduce repeated setup. `AgentSpec` can override those defaults for an analyst, a writing agent or another workload without changing its siblings. Temperature and token limits are forwarded to the selected native model request, rather than being merely descriptive builder values.\n- [Runtime](docs/gitbooks/en/developing/embed/concepts/runtime.md): A `Runtime` boots the in-process core once. It owns the selected domains, background services, module host and runtime defaults. Its agents share that core while using separately derived contexts. Build one runtime and pass an `Arc<Runtime>` to the parts of your application that register agents.\n- [Skills](docs/gitbooks/en/developing/embed/concepts/skills.md): A skill bundle contains `SKILL.md` plus any referenced resources. `AgentSpec::skills_dir` copies bundles into the agent's `agents/<id>/skills/` tree. Copying is intentional: discovery rejects symlinked bundles, so linking a shared directory does not install it.\n- [Testing](docs/gitbooks/en/developing/embed/concepts/testing.md): The runnable examples default to loopback fixtures. They assert request bodies, tool results, file effects, transcript scope and event ordering; a successful Rust compilation alone does not prove those behaviors. Live example execution is explicitly opt-in through the documented environment variables.\n- [Tools](docs/gitbooks/en/developing/embed/concepts/tools.md): An agent's `ToolScopeSpec` chooses built-ins, named tools or `HostOnly`. Host-only mode starts with the host's advertised tools, requires an explicit bare prompt, and does not inherit an orchestrator tool catalog. This is useful when your application already owns read-only data access or tightly scoped actions.\n- [Turns and sessions](docs/gitbooks/en/developing/embed/concepts/turns-sessions.md): `Agent::run` collects a turn. `Agent::turn` lets you set an explicit session, per-turn options, progress sink and cancellation before sending. Reuse the returned session identity for follow-up messages that should see the same transcript. Two calls on the same durable session remain distinct turn executions and receive distinct observation IDs.\n- [Embedding FAQ](docs/gitbooks/en/developing/embed/faq.md): An ordinary embed process supports one `Runtime`; a second build returns `AlreadyRunning`. Add agents to the existing runtime. A multi-user host uses `ProfileRuntime` instead and needs a process dedicated to that mode. See runtime and profiles.\n- [Embedding guides](docs/gitbooks/en/developing/embed/guides/README.md): Choose the guide that matches your host:\n- [CLI chatbot](docs/gitbooks/en/developing/embed/guides/cli-chatbot.md): Keep one `Runtime` alive for the process and create an `Agent` for the conversation. The executable example starts with a single message, checks the reply, and prints it. A CLI input loop can send subsequent messages through the same agent; choose a stable thread identity when your host exposes separate conversations.\n- [Deploy a server](docs/gitbooks/en/developing/embed/guides/deploy-server.md): Your HTTP host can keep a runtime alive and invoke its agents directly. The executable example binds a loopback listener, handles one request, and sends back the agent's reply. It exercises the request path without requiring an RPC server dependency in `openhuman-embed`.\n- [Lean and headless hosts](docs/gitbooks/en/developing/embed/guides/lean-headless.md): `RuntimeBuilder::lean()` selects the agent, memory, and inference families without background services. Runtime module selection controls what is active; Cargo features control what is compiled. A lean preset alone does not shed compiled dependencies.\n- [Test with a mock backend](docs/gitbooks/en/developing/embed/guides/mock-backend-testing.md): Run `node scripts/run-embed-examples.mjs` from the repository root. The runner builds and executes every example with the union of required features, clears inherited live-example credentials and operator workspace settings, and requires each program's `EXAMPLE_OK` marker after its assertions.\n- [Multiple agents](docs/gitbooks/en/developing/embed/guides/multi-agent.md): A runtime owns shared services and credentials. Each `AgentSpec` selects its prompt, model route, access policy, and `action_dir`. Internal agent state belongs to its agent home; the action directory is where acting tools operate.\n- [SaaS with multiple tenants](docs/gitbooks/en/developing/embed/guides/saas-multi-tenant.md): Use `ProfileRuntime` for a process serving multiple users. Provision each profile, install that profile's credential, and open a handle to its workspace. Reusing a thread id in two profiles does not share their conversation history.\n- [Installation](docs/gitbooks/en/developing/embed/installation.md): `openhuman-embed` is currently an unpublished workspace crate. Use a local path dependency when developing in this repository, or a Git dependency on `https://github.com/tinyhumansai/openhuman` with `package = \"openhuman-embed\"`. Pin a reviewed Git revision for a deployed application; the nested vendored libraries are part of that revision's build inputs.\n- [Embedding integrations](docs/gitbooks/en/developing/embed/integrations/README.md): An embedded runtime composes the capabilities its host supplies. Start with the adapter your application needs:\n- [Channels](docs/gitbooks/en/developing/embed/integrations/channels.md): Enable the `channels` Cargo feature to use channel adapters. Your host configures the channel credential and binds incoming messages to an agent. In the Telegram example, the Bot API endpoint is a loopback stub, so updates and replies exercise the adapter without contacting Telegram.\n- [Composio](docs/gitbooks/en/developing/embed/integrations/composio.md): Composio calls use the host backend transport. Install that transport before booting a managed runtime; see TinyHumans managed inference. An explicit inference provider route supplies model inference, not the connector transport.\n- [Local and custom models](docs/gitbooks/en/developing/embed/integrations/local-models.md): For a local server that implements chat completions, use `Provider::openai_compatible` with its API root and model id. The constructor requires a bearer string even when a particular local server ignores it. Set the endpoint explicitly rather than relying on another user's installed configuration. The OpenAI-compatible example controls can point a live example at that local server.\n- [MCP servers](docs/gitbooks/en/developing/embed/integrations/mcp.md): Enable the `mcp` Cargo feature and declare a server with `AgentSpec::mcp`. `McpServer::http` takes a registered name and endpoint; `allow_tools` narrows the remote tool list. Server configuration and its connections belong to the agent that declared them.\n- [OpenAI-compatible providers and BYOK](docs/gitbooks/en/developing/embed/integrations/openai-compatible.md): Construct a route with `Provider::openai_compatible(base_url, api_key).model(model_id)`, then set it on the runtime or an individual agent. Pass the API root, such as a provider's `/v1` URL; the adapter appends `/chat/completions`. Supply an explicit model id so the route can register that model for its workload roles.\n- [Storage drivers](docs/gitbooks/en/developing/embed/integrations/storage-drivers.md): Enable `storage-sqlite` for SQLite or `storage-mongodb` for MongoDB on your Embed dependency. These Cargo gates make the driver available; choosing a URL selects the runtime backend. Set `RuntimeConfig.storage.url`, pass a URL through `RuntimeBuilder::storage`, or set the `url` field in the `[storage]` section of the host's configuration. `OPENHUMAN_STORAGE_URL` is the environment override used by host configuration resolution.\n- [TinyHumans managed inference](docs/gitbooks/en/developing/embed/integrations/tinyhumans-managed.md): Embed wraps the core; neither contains a TinyHumans backend client. A managed host boots through the builder in `openhuman-tinyhumans` (`openhuman_tinyhumans::RuntimeBuilder`), or calls `openhuman_tinyhumans::install`. That layer owns the transport, the authenticated client, the hosted routes and the hosted providers, and it plugs them into the core through the core's generic registries (chat models, voice, tool sources, the channel callback and memory engines). A host can register its own provider in the same registries. The connected host layer also owns session login. `RuntimeBuilder::backend_url` selects a URL; it does not connect anything.\n- [Web search](docs/gitbooks/en/developing/embed/integrations/web-search.md): An agent's web-search tools (`web_search_tool`, `web_answer_tool`, `web_contents_tool`) run through the process-wide TinySearch module. Several agents in one runtime can each search with their own provider key: pin it on that agent's config, and it is never written to `config.toml`, the environment, or a log.\n- [Quickstart](docs/gitbooks/en/developing/embed/quickstart.md): Start from a checkout of the repository and run `cargo run -p openhuman-embed --example run_turn`. The example boots an ephemeral runtime, creates its own local provider/backend fixtures and prints `hello from the stub`. It needs no inference account and does not contact a real backend.\n- [Embedding troubleshooting](docs/gitbooks/en/developing/embed/troubleshooting.md): Use the typed error and the effective capability report together. A method that was compiled out is a different problem from a configured method with a provider failure. Keep credentials, user prompts and raw provider payloads out of routine diagnostics.\n- [OpenHuman Embed](crates/openhuman-embed/README.md): A typed Rust facade for running the OpenHuman core inside your application: one Runtime per process, with independently configured Agents.\n- [Embedding OpenHuman](docs/gitbooks/en/developing/embedding.md): The embedding documentation now lives in the Embedding section.\n- [Cookbook](docs/gitbooks/en/developing/embed/cookbook.md): Executable recipes are indexed from their source headers. Each entry records the declared run mode and any named Embed feature required by the example. Full source includes the surrounding setup and assertions.\n- [Compiled feature matrix](docs/gitbooks/en/developing/embed/capability-matrix.md): This matrix comes from the default-feature compiled capability-report example. Runtime presets can narrow enabled domains and services; core Cargo features determine which implementations are available. Named Embed features also gate facade exports: enable `mcp` or `skills` explicitly for their public setters even when core defaults compile those implementations. Embed defaults enable the named `channels` feature. See the installation guide for these separate layers.\n- [RuntimeBuilder setters](docs/gitbooks/en/developing/embed/builder-setters.md): Public consuming methods that return `Self`, extracted from the RuntimeBuilder implementations. Static host and weight presets, inspection methods and build/run methods are excluded.\n- [API index](docs/gitbooks/en/developing/embed/api-index.md): Generate the complete local Rust API reference with `cargo doc -p openhuman-embed --all-features --no-deps --open`. This crate is currently unpublished; docs.rs is a future publication target. Names below are source-listed exports, including conditional Cargo-gated items; local rustdoc with your selected features describes availability. Canonical facade source.\n- [Embed examples](crates/openhuman-embed/examples/README.md): Cookbook\n\n<!-- File: CLAUDE.md (Tokens: ~12170 | Category: claude-rule) -->\n# OpenHuman\n\nOpenHuman is a React and Tauri v2 desktop assistant with an in-process Rust\ncore. The core also exposes JSON-RPC and a CLI.\n\nArchitecture: [overview](docs/gitbooks/en/developing/architecture.md),\n[frontend](docs/gitbooks/en/developing/architecture/frontend.md),\n[Tauri shell](docs/gitbooks/en/developing/architecture/tauri-shell.md), and\n[agent harness](docs/gitbooks/en/developing/architecture/agent-harness.md).\n\n## Repository map\n\n| Path | Purpose |\n| --- | --- |\n| `app/src/` | Vite and React frontend |\n| `crates/openhuman-app/` | Thin desktop host; excluded from the root workspace, build with `--manifest-path crates/openhuman-app/Cargo.toml`. Depends on `openhuman-rpc` only and boots its in-process core with `openhuman_rpc::host::desktop` |\n| `crates/openhuman-core/` | Package `openhuman`: business domains under `src/<domain>/`, the controller contract, dispatch and auth under `src/core/` |\n| `crates/openhuman-core/src/<domain>/` | Flat business-domain modules (agent, memory, tools, security, channels, ...) |\n| `crates/openhuman-core/src/storage/` | The process's storage backend on the `tinystoragedrivers` ports: URL resolution (`OPENHUMAN_STORAGE_URL` / `[storage] url`), the install slot, agent scopes (`local` for the operator), and the base the domain stores build on. Features `storage-sqlite`, `storage-file`, `storage-mongodb`; see `docs/gitbooks/en/developing/architecture/storage.md` |\n| `crates/openhuman-core/src/core/` | CLI, controller contract (`Outcome`, schemas) and in-process dispatch, controller registry, event bus, runtime composition; no business logic and no JSON-RPC server |\n| `crates/openhuman-cli/` | The `openhuman-core` binary (`src/main.rs`, `openhuman_rpc::host::cli`), the ops bins (`src/bin/`: `openhuman-fleet`, `test-mcp-stub`), and every root `tests/*.rs` / `examples/*.rs` target. Normal dependency: `openhuman-rpc` only; the tests reach core, embed and tinyhumans through `[dev-dependencies]`. The benchmark bins live in the openhuman-benchmarks repository |\n| `crates/openhuman-embed/` | Library facade over the core (depends on core only): `Runtime`/`RuntimeBuilder` with host presets, `embed::process` (tokio runtime, logging, dotenv, master key, Sentry options), and the curated facades hosts use (`config`, `artifacts`, `chat_surface`, `modules`, `identity`). Its doc-hidden `__host` list is for tinyhumans and rpc only |\n| `crates/openhuman-rpc/` | Top of the library chain (depends on tinyhumans only). JSON-RPC 2.0 over the core: envelopes, HTTP client, and the server (router, Socket.IO, listener, `run_server*`); `host::{cli, desktop, tui}`, the shared host boot; re-exports `embed` and `tinyhumans` as the hosts' curated facade; plus `session_store` (`session-store` feature), the on-disk session store (`session_raw/`, `session_db/`, `tinyagents_store/`, turn states) behind TinyAgents' session store port, which the app, CLI and TUI install. Core and embed reach session state through the port (`agent::session_store`); a few legacy paths still fall back to workspace files when no store is installed. With a storage URL (`OPENHUMAN_STORAGE_URL` / `[storage] url`), `install_for_host` installs TinyAgents' `DriverSessionStores` over that backend instead (core `storage` domain) |\n| `crates/openhuman-tinyhumans/` | The TinyHumans layer above embed (depends on embed only), and the only owner of anything hosted: the backend transport port and its SDK implementation, the authenticated client, routes and RPC proxies, and the hosted providers (integrations, voice, inference, channels, memory), all plugged into the core through its generic registries; a `RuntimeBuilder` that boots connected; and the host-side login/session owner (login-token exchange, `/auth/me`, current-user cache, credential handoff) used by app and TUI |\n| `crates/openhuman-tui/` | Standalone terminal frontend; depends on `openhuman-rpc` only and boots with `openhuman_rpc::host::tui` |\n| `tests/` | Rust integration and JSON-RPC tests |\n| `docs/gitbooks/` | Public product and contributor documentation, synced with GitBook (`en/` is canonical, `zh/` the translation) |\n| `docs/` | Everything else here is internal maintainer documentation |\n| `vendor/` | Recursive git submodules; root `Cargo.toml` `[patch]` tables point into this tree |\n\nRun commands from the repository root. The root package is a private pnpm\nworkspace.\n\nThe Rust crates form a strict chain; each one's normal dependencies name only\nthe layer directly below it:\n\n```text\nopenhuman-core -> openhuman-embed -> openhuman-tinyhumans -> openhuman-rpc -> { app, cli, tui }\n```\n\nHosts (app, CLI, TUI) depend on `openhuman-rpc` alone and reach the core\nthrough its curated facade (`openhuman_rpc::host`, `openhuman_rpc::embed`,\n`openhuman_rpc::tinyhumans`), never through `__host` / `core_host` or an\n`openhuman_core::` path. `node scripts/ci/check-crate-chain.mjs` (part of\n`pnpm rust:layout`) enforces both. Dev-dependencies are exempt, which is how\nthe root tests keep reaching into the core.\n\n## Product boundaries\n\n- The shipped Tauri product targets Windows, macOS, and Linux.\n- The experimental iOS client is not part of the shipped desktop host. Its\n  transport implementations live in `app/src/services/transport/`.\n- The Rust core owns business rules, persistence, execution, RPC, and CLI\n  behavior.\n- The frontend and Tauri shell present or orchestrate core behavior. Do not\n  duplicate core policy in TypeScript or shell code.\n- The desktop core runs as a tokio task managed by\n  `crates/openhuman-app/src/core_process.rs` (`openhuman_rpc::host::desktop`).\n  Frontend RPC uses the per-launch bearer returned through the\n  `core_rpc_token` command.\n- `OPENHUMAN_CORE_REUSE_EXISTING=1` connects the shell to an external core for\n  debugging.\n\n## Common commands\n\n```bash\npnpm install\npnpm dev\npnpm dev:app\npnpm build\npnpm typecheck\npnpm lint\npnpm format\npnpm format:check\npnpm test\npnpm test:coverage\npnpm test:rust\n\ncargo check --manifest-path Cargo.toml\ncargo build --manifest-path Cargo.toml -p openhuman-cli --bin openhuman-core\ncargo check --manifest-path crates/openhuman-app/Cargo.toml\n\n# Standard root-crate validation\ncargo check --manifest-path Cargo.toml\n```\n\nUse the summary-sized debug runners for long test output:\n\n```bash\npnpm debug unit [test-file]\npnpm debug unit -t \"test name\"\npnpm debug e2e [spec]\npnpm debug rust [filter]\npnpm debug logs last\n```\n\nDebugging the web UI in a real browser (Chrome DevTools MCP): the desktop\nwindow is Wry (WKWebView / WebView2 / WebKitGTK), which does not support CDP, so no\nDevTools client can attach to it. Run the same SPA in Chrome instead:\n\n- **Use the desktop app's session:** `pnpm dev:app:web:attach -- --no-browser`\n  finds the running desktop core, starts Vite, and prints\n  `http://127.0.0.1:<core>/dev/connect?app=http://localhost:<vite>`. Open that\n  URL with the chrome-devtools MCP (`new_page` / `navigate_page`). The core's\n  dev-only `GET /dev/connect` (`crates/openhuman-rpc/src/server/dev_connect.rs`)\n  redirects to Vite's `/__dev-connect` page with the RPC URL and bearer in the\n  URL fragment. That page seeds them into `localStorage`, so the browser runs on\n  the desktop core with its signed-in user, and nothing is pasted. The route\n  exists in debug builds, or in a release build with `OPENHUMAN_DEV_CONNECT=1`.\n  It accepts only a loopback `app` origin and `Host`, and refuses cross-site\n  navigations.\n- **Fresh core instead:** `pnpm dev:app:web --no-browser` builds and starts its\n  own `openhuman-core serve` with a generated bearer and prints\n  `http://localhost:<vite>/__dev-connect`. Sign-in takes one click on a provider:\n  the browser build passes `<origin>/__dev-auth` as the backend `redirectUri`,\n  and the session persists in that core's workspace.\n- Busy ports are fine. Vite moves to the next free port, and so does\n  `openhuman-core serve`, even when another live core holds the port.\n  (`OccupiedByCore::Fallback`; only the desktop shell's embedded core runs the\n  stale-listener takeover.) The printed URL always uses the real ports.\n- Onboarding and the walkthrough tour are skipped by default\n  (`VITE_DEV_SKIP_ONBOARDING`, marked complete in the core for a signed-in\n  user). Pass `--onboarding` to keep them for debugging.\n- Inspect with `take_snapshot`, `list_console_messages`,\n  `list_network_requests` (check the `/rpc` calls), `evaluate_script`, and\n  `take_screenshot`.\n- Env settings: `OPENHUMAN_DEV_PORT`, `OPENHUMAN_CORE_PORT` (in attach mode, the\n  desktop core's port; the default scans 7788-7808), `OPENHUMAN_CORE_TOKEN`, and\n  `OPENHUMAN_WORKSPACE` (point it at a scratch dir for a clean profile).\n\nScripted browser runs (Playwright, headless): `pnpm debug web` boots a\nthrowaway stack (the mock backend, a fresh `openhuman-core serve` on a scratch\nworkspace, Vite with `OPENHUMAN_VITE_NO_WATCH=1`) and signs in through the real\nGitHub button, which the mock answers like the backend. `--script <file.mjs>`\nruns a scenario against the signed-in page (default export receives `page`,\n`mock.set(key, value)` for mock behaviors such as `llmStreamScript`, `rpc`,\n`screenshot` and `log`); without it the stack stays up until Ctrl-C. Logs and\nscreenshots go to `target/debug-logs/web-<ts>/`. Example:\n`scripts/debug/web-scripts/stop-mid-turn.mjs`. Set `OPENHUMAN_VITE_NO_WATCH=1`\non any Vite run that dies with `ENOSPC: System limit for number of file\nwatchers reached`. `--headed` needs the full Chromium build\n(`pnpm --filter openhuman-app exec playwright install chromium`).\n\nLong CI build or test commands must run through\n`scripts/ci-cancel-aware.sh`. Do not export `CARGO_TARGET_DIR`; the repository\nalready configures shared build output where appropriate.\n\nKeep matching profile settings synchronized between `Cargo.toml` and\n`crates/openhuman-app/Cargo.toml`:\n\n- Development dependencies use `debug = false`.\n- Release builds use thin LTO, 16 codegen units (one unit pushed the desktop\n  release matrix from ~46 to ~84 min, 6941b18c85), symbol stripping, and\n  `debug = \"line-tables-only\"`.\n\n## Testing and CI\n\nCI Lite runs area-specific checks and changed-line coverage on PRs to `main`\nor `release`. CI Full runs the complete suites for `release`. Changed-line\ncoverage must be at least 80 percent.\n\n### Test ownership\n\nOpenHuman tests composition, host policy, adapters, RPC wire contracts, and data compatibility. Module behavior is tested in the owning `vendor/tiny*` repository. A host test that calls a vendor module's internals (for example, `tinychannels::providers::*` or `tinyinference_local::service::*`) belongs upstream. When code moves upstream, move its tests in the same upstream PR, then delete them in the host PR. Keep one host smoke test per module: load it and make one call through the bus. That is enough to test the host integration.\n\nA PR that only deletes tests is exempt from changed-line coverage because it has no changed lines for diff-cover to measure. CI Lite runs that changed-line check at an 80 percent threshold.\n\n- Frontend unit tests are colocated as `*.test.ts` or `*.test.tsx` under\n  `app/src/`. Use Vitest and test behavior rather than implementation.\n- Rust unit tests are never inline. Put them in a sibling `<module>_tests.rs`\n  (`mod_tests.rs` beside a `mod.rs`) declared at the bottom of the module with\n  `#[cfg(test)]` and `#[path = \"<module>_tests.rs\"]` above `mod tests;`. The file\n  starts with `use super::*;` and carries no `#[cfg(test)]` of its own. Never name\n  one `test.rs`, `tests.rs` or `<module>_test.rs`, and never write an inline\n  `#[cfg(test)] mod tests { ... }` (`pnpm rust:layout` fails on an inline module\n  and on `test.rs`/`tests.rs`/`<module>_test.rs`). The same rule binds every\n  `vendor/` submodule: `node scripts/externalize-inline-tests.mjs <repo-root>\n  --write` converts one mechanically (add `--rename-legacy` for `test.rs` and\n  `<module>_test.rs`), and a crate root directly in\n  `src/bin/` keeps its tests in `src/bin/<stem>/` because Cargo builds any `.rs`\n  placed straight in `src/bin/` as a binary.\n- Rust domain tests live beside their modules. Use\n  `scripts/test-rust-with-mock.sh` for tests that need the shared mock backend.\n- JSON-RPC behavior belongs in Rust E2E tests, commonly\n  `tests/json_rpc_e2e.rs`.\n- Frontend flows need mocked browser or desktop E2E coverage under\n  `app/test/e2e/specs/`.\n- E2E code must use `app/test/e2e/helpers/element-helpers.ts`, not raw platform\n  element types.\n- Tests must not call real backend or third-party services.\n- Avoid time-based flakes and real network access in unit tests.\n- Root `tests/*.rs` and `examples/*.rs` are targets of **`crates/openhuman-cli`**\n  (the core is a library and declares no bin/test/example targets). They are\n  NOT auto-discovered (`autotests = false`, `autoexamples = false`); every new\n  file needs an explicit `[[test]]` / `[[example]]` entry in\n  `crates/openhuman-cli/Cargo.toml` with `path = \"../../tests/<name>.rs\"`;\n  `pnpm rust:layout` (`scripts/ci/check-openhuman-rust-layout.mjs`) fails on\n  missing or stale entries, on any target table in the core manifest, on\n  inline `#[cfg(test)] mod` blocks, and on files named `tests.rs`/`test.rs`.\n  Files under `tests/raw_coverage/` are aggregated by the root `build.rs`\n  (`build = \"../../build.rs\"`) into the single `raw_coverage_all` target and\n  need no entry. Run them as `cargo test -p openhuman-cli --test <name>`.\n- A suite that boots the core **in-process** and reaches the backend (mock)\n  must call `tinyhumans_boot::boot()` from `tests/support/tinyhumans_boot.rs`\n  first (it runs `openhuman_tinyhumans::install`, a dev-dependency of\n  `openhuman-cli`); the core has no hosted code of its own, and without it\n  every backend call answers `BACKEND_UNAVAILABLE:`. Suites that spawn the\n  `openhuman-core` binary get it from `main.rs` (`openhuman_rpc::host::cli`).\n\nShared mock backend:\n\n- Core routes: `scripts/mock-api-core.mjs`\n- Server: `scripts/mock-api-server.mjs`\n- E2E adapter: `app/test/e2e/mock-server.ts`\n- Manual start: `pnpm mock:api`\n\nDebugging what the core actually sends to inference (prompt size, tool\nschemas, cache keys, which endpoint answered, time to first byte, cached\ntokens): put the capture proxy between the core and its backend instead of\nguessing from logs.\n\n- `CAPTURE_ALL=1 pnpm debug capture` (`scripts/debug/capture-first-inference.mjs`)\n  listens on `127.0.0.1:18765`, forwards everything to `CAPTURE_UPSTREAM`\n  (default `https://api.tinyhumans.ai`; `https://openrouter.ai` for a direct\n  BYOK route), dumps every inference request body under\n  `target/debug-logs/inference-sequence/`, and prints one line per response:\n  `served_by`, `ttfb`, `prompt`, `cached`, `cache_key`, status, error.\n- Point a core at it with `api_url = \"http://127.0.0.1:18765\"` in the user\n  `config.toml` or `BACKEND_URL=http://127.0.0.1:18765` on a headless\n  `openhuman-core run`; drive turns over JSON-RPC (`channel_web_chat`).\n- Read the lines as claims to check: `cache_key` must be identical across the\n  turns of one thread, `served_by` should not change mid-thread, and `cached`\n  should approach `prompt` from the second call on. Any of those drifting is\n  the finding.\n- Self-test: `scripts/__tests__/capture-first-inference.test.mjs` (runs in the\n  CI scripts lane). The static prompt on its own comes from\n  `openhuman-core agent dump-prompt --agent <id> --json --with-tools`\n  (`scripts/debug-agent-prompts.sh`); the proxy shows the request the harness\n  assembles from it per turn.\n- `pnpm debug breakdown <req-NNN.json> --response <res-NNN.txt>`\n  (`scripts/debug/prompt-breakdown.mjs`) prices one captured request: each\n  system-prompt section, each tool schema (description vs parameters) and each\n  message, in o200k tokens calibrated to the provider's `usage.prompt_tokens`,\n  plus paragraphs the request pays for twice. Capture with\n  `CAPTURE_ALL=1 CAPTURE_RESPONSES=1` so the response carries the usage.\n\n## Configuration and security\n\n- Copy environment settings from `.env.example` and `app/.env.example`.\n- Frontend environment access is centralized in `app/src/utils/config.ts`.\n  Do not read `import.meta.env` elsewhere.\n- Rust configuration is defined under\n  `crates/openhuman-core/src/config/schema/` and loaded through its config operations.\n\nThe autonomy policy is **off by default** (`[autonomy] enabled = false`,\n`config/schema/autonomy.rs`). Agents here run inside containers, platform jails\nand Docker sandboxes that already provide the isolation this in-process policy\nwas approximating, and a shell tool that refuses ordinary shell syntax is not a\nusable shell. `SecurityPolicy::from_config` carries the flag; every enforcement\nentry point short-circuits on it.\n\nWith the policy **disabled** (the default):\n\n- Command classification, the approval gate, the command allowlist and the\n  hourly action budget are all inert.\n- `workspace_only`, `forbidden_paths` and the workspace-internal boundary are\n  not enforced.\n- `is_always_forbidden` still is: credential stores (`~/.ssh`, `~/.gnupg`,\n  `~/.aws`) and system roots stay unreachable. So do `..` traversal and null\n  bytes in a path. Keep that floor.\n\nWith `[autonomy] enabled = true`:\n\n- `action_dir` is the agent's permitted read and write root — and is granted as\n  a `ReadWrite` trusted root by `from_config`, so changing the working folder\n  does not cost the agent write access to it.\n- `workspace_dir` stores internal state and is never an acting-tool target.\n- Unknown commands classify as writes.\n- The approval gate prompts on non-read classes. Interactive requests expire as\n  denied after ten minutes.\n\nEither way, sandboxed agents use the platform jail or Docker backend, and the\nRust path checks still apply if the sandbox falls back.\n\nA quoted heredoc body (`<< 'EOF' … EOF`) is **data**, not shell:\n`strip_quoted_heredoc_bodies` blanks it before any structural scan. An unquoted\ndelimiter (`<< EOF`) is still expanded and still scanned.\n\n## Frontend\n\nThe provider chain is documented and generated from `app/src/App.tsx`.\nUpdate the source marker and run `pnpm docs:generate`; do not hand-edit\ngenerated documentation blocks.\n\n- Redux Toolkit is the default state layer. The authoritative slice list is in\n  `app/src/store/index.ts`.\n- Persist user state through `userScopedStorage`, not ad hoc\n  `localStorage`.\n- Use `coreRpcClient` for core RPC. It `fetch()`es the loopback core\n  directly and routes only non-loopback plain-`http://` runtimes (blocked as\n  mixed content, #3865) through the `relay_http_rpc` Tauri command.\n- Auth state comes from `CoreStateProvider` and\n  `fetchCoreAppSnapshot()`.\n- Routes are defined in `AppRoutes.tsx`. Check that file before adding links\n  or redirects.\n- Bundled agent prompts live under `crates/openhuman-core/src/agent/prompts/`, not in the\n  frontend.\n\nAnalytics:\n\n- Shared buttons use a stable, content-free `analyticsId`.\n- Successful domain outcomes use `trackAnalyticsEvent` from\n  `components/analytics`.\n- Never send user text, entity IDs, filenames, credentials, or error messages.\n\nUI rules:\n\n- Use `useT()` for user-facing text and add real translations for every\n  locale.\n- Preserve interpolation placeholders across translations.\n- Run `pnpm i18n:check`, `pnpm i18n:english:check`, and the i18n coverage\n  test.\n- Do not use dynamic imports in production `app/src`.\n- Use `isTauri()` or catch `invoke` failures. Do not inspect\n  `window.__TAURI__` directly.\n- Canonical visual tokens live in `app/src/styles/tokens.css`.\n- Always build UI from the shadcn primitives in `app/src/components/ui/`\n  (`Button`, `Badge`, `Alert`, `Card`, `ModalShell`/`Dialog`, `Popover`,\n  `Tooltip`, `Tabs`/`ChipTabs`, `TextField`, `NativeSelect`, `Toast`, ...).\n  Do not hand-roll a surface a primitive already covers (a banner is an\n  `Alert`, a dismiss control is a `Button`, a notification is `toast.add`).\n  When a primitive is missing, add it to `components/ui/` from the shadcn\n  registry (`components.json`, `base-nova` style), adapted to the app's\n  tokens, `Button` and lucide icons, then use it.\n\n## Tauri shell\n\nKeep `crates/openhuman-app/` thin. The authoritative IPC list is the\n`generate_handler!` call in `crates/openhuman-app/src/lib.rs`.\n\nDo not add JavaScript injection to child webviews. New behavior belongs in\nRust-side IPC hooks. Audit new Tauri plugins for `js_init_script`.\n\nThe app uses Wry. Do not restore CEF or CDP scanner assumptions. The native\niMessage scanner remains separate because it reads `chat.db` directly.\n\n## Rust domain structure\n\nBusiness logic belongs under `crates/openhuman-core/src/<domain>/`. Do not add flat\n`crates/openhuman-core/src/*.rs` domain files or business logic to\n`crates/openhuman-core/src/core/`.\n\nPreferred module shape:\n\n| File | Purpose |\n| --- | --- |\n| `mod.rs` | Module declarations, re-exports, and controller aggregators |\n| `types.rs` | Serde domain types |\n| `store.rs` | Persistence |\n| `ops.rs` | Business operations returning `Outcome<T>` |\n| `schemas.rs` | Controller schemas and thin handlers |\n| `tools.rs` | Domain-owned agent tools |\n| `bus.rs` | Event subscribers |\n| `*_tests.rs` | Focused behavior tests |\n\nAdditional rules:\n\n- Wire controllers through the registry in `crates/openhuman-core/src/core/all.rs`. Do not add\n  namespace branches to `cli.rs` or the JSON-RPC server.\n- RPC namespace strings are wire contracts and do not follow directory\n  renames.\n- Domain tools live with their domain. Each domain builds its tool family\n  through a registration function; `tools::ops` composes those families before\n  applying capability filters. Preserve catalogue order when moving\n  registrations. Import concrete tools from their owning domain instead of\n  adding domain globs to `tools/mod.rs`. Keep only cross-cutting tools in\n  `tools/impl/`.\n- Memory scope comes from the namespace, never from metadata or model\n  arguments: `memory::scope` resolves the acting identity to a layout root\n  and memory agent id, and under layout v3 the engine is bound with\n  `EngineSettings::scope_root` = `user:<id>` (`memory::scope::user_root`),\n  below which TinyMemory places `ws:`/`source:`/`agent:` nodes and\n  `app:<kind>` leaves (`vendor/tinymemory/docs/architecture/cortex-layout.md`).\n  Item ids are deduplication keys.\n- Update `crates/openhuman-core/src/platform/about_app/` when user-visible capabilities\n  change.\n- The controller contract lives in core: every domain operation returns\n  `crate::core::Outcome<T>`; `crate::core::StructuredRpcError`, the params\n  rules (`core::params`) and in-process dispatch (`core::invoke::invoke_method`)\n  sit beside `ControllerSchema`. Core has no JSON-RPC server and does not\n  depend on `openhuman-rpc`.\n- `crates/openhuman-rpc/` sits above core and owns JSON-RPC 2.0: the envelopes\n  (`RpcRequest`, `RpcSuccess`, `RpcFailure`, `request_body`,\n  `decode_response`), the browser-origin allowlist, the HTTP client\n  (`http-client` feature), and the whole server (`server` feature): the axum\n  router and handlers, auth middleware, Socket.IO, `/dev/connect`, the\n  listener bind (`openhuman_rpc::server::serve`) and the `run_server*` entry\n  points. `openhuman_rpc::host::cli` gives the core this crate's server as\n  the `run`/`serve` launcher.\n  Domain-owned HTTP handlers the router mounts (`inference::http`, the\n  dictation WebSocket) stay in their domains behind core's `http-server`\n  feature. The `http_host` static-directory file server lives here too\n  (`openhuman_rpc::http_host`); `host::cli`, `host::desktop` and\n  `build_core_http_router()` register its `http_host.*` controllers as a core\n  extension, so a host without this crate has no `http_host` surface.\n- The hosts boot through `openhuman_rpc::host`: `host::cli(args)` is the\n  `openhuman-core` binary (and the app's `core` / `mcp` subcommands);\n  `host::desktop(DesktopOptions, shutdown, ready_tx)` is the desktop shell's\n  embedded server (in-memory bearer, preferred port with stale-listener\n  takeover, ready signal); `host::tui()` builds the TUI's runtime. Each\n  connects the TinyHumans backend itself. One embed runtime exists per\n  process, so a host that restarts its server must let the old task (and the\n  runtime it owns) drop before it spawns the next.\n\n## Tool, harness, and runtime boundaries\n\nEmbed public API documentation: [Embedding](docs/gitbooks/en/developing/embed/README.md),\n[concepts](docs/gitbooks/en/developing/embed/concepts/README.md), and the source-generated\n[builder setters](docs/gitbooks/en/developing/embed/builder-setters.md). Snippets come from\ncompiled examples; run `pnpm docs:generate` and `pnpm docs:check` after changing them.\n\n\n`tinyagents` owns tool-call dialects, parsing, catalog rendering, transcript\nreplay, session identity, and the agent loop. `tinytools` owns the shared\n`Tool` trait and tool types. OpenHuman owns execution policy, approvals,\nsandboxing, timeouts, and progress events.\n\n- Use the `tinytools` copy vendored through `vendor/tinyagents/`; a second path\n  creates incompatible Rust types.\n- Keep conversions mechanical. Policy decisions belong in OpenHuman.\n- **Put a change in the repo that owns it, not where it is easiest to land.**\n  Tool-call parsing, grammars, the `Tool` trait and generic tool types go to\n  `vendor/tinyagents/vendor/tinytools`; the agent loop, dialects, prompt\n  cache layout, run policy, progress events and generic harness tools (the\n  session todo list, goals, delegation graph) go to `vendor/tinyagents`\n  (`tinyagents-harness` / `tinyagents-graph`); OpenHuman keeps only the host\n  adapters (scope, dispatch, approvals, progress projection). Open the\n  upstream PR in that repo first, then move the gitlink here. A host-side\n  workaround for a harness or parser bug is a stopgap, not a fix: file or\n  fix it upstream in the same PR.\n- `openhuman_embed::Runtime` → `Agent` is the public library API: one runtime\n  per process (features, services, backend URL, TinyHumans API key), then any\n  number of independently configured agents on it (`AgentSpec`: provider,\n  access, `action_dir`, MCP servers, skills, prompt, tool scope, sandbox).\n  `Harness` is the one-agent shorthand over the same two types. Agent turns\n  dispatch natively (`inference::host_runtime::ops::agent_chat_for`) under the\n  agent's own `CoreContext` (`CoreContext::derive_with`); other facade calls\n  go through `CoreRuntime::invoke`.\n- Set `config_path` with `workspace_dir`, and set a turn origin with its access\n  tier. `Access::full()` configures both access fields. Every agent on a\n  runtime shares its `config_path` (credentials, keyring, API key).\n- Copy skills into an agent's `agents/<id>/skills/` (what\n  `AgentSpec::skills_dir` does) because skill discovery rejects symlinked\n  bundles. Library agents hide the operator's `~/.openhuman/skills` unless\n  `include_user_skills(true)`.\n- Library mode has no user login: the runtime's API key rides managed\n  inference as `Authorization: Bearer` and backend REST as `x-api-key`\n  (`security::credentials::api_key`, `session_support::BackendCredential`).\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n  Every backend caller resolves its credential through\n  `resolve_backend_credential` (or `backend_bearer_secret` for bearer-only\n  seams), never `get_session_token`, so the key covers integrations, voice,\n  embeddings, memory-host and socket calls too; only `/auth/*` session flows\n  need a signed-in user.\n- The core never obtains, validates, exchanges or refreshes a credential.\n  It takes one — a session JWT, an API key, or the offline local token —\n  through `auth.set_credential` (`security::credentials::ops::credential`)\n  and does only what it owns with it: user-dir activation, gated services,\n  the scheduler gate, Sentry and prompt identity. Login-token exchange,\n  `GET /auth/me` and the current-user cache belong to the host's session\n  owner: `openhuman_tinyhumans::session` behind the Tauri shell's `auth_*`\n  commands and the TUI, `openhuman_embed::Auth` for embedders, the CLI or\n  `OPENHUMAN_BACKEND_API_KEY` / `OPENHUMAN_BACKEND_SESSION_TOKEN` for\n  headless hosts. Do not add backend auth endpoints back to the core.\n\n### Sessions\n\nA conversation's durable identity is `tinyagents_session::transcript::SessionRef`,\nderived from the thread id and the agent id. It maps to a transcript stem\ndeterministically and **without a timestamp**, so one conversation resolves to\none file in every process and on every launch. OpenHuman binds it in\n`set_thread_id` and resumes with `ResumeMode::Session`; it does not mint stems,\nseed history by hand, or pick a transcript by recency.\n\n- **The transcript is what the model sees.** Trimming it is legitimate.\n- **A compaction never erases.** It seals the current generation and opens the\n  next (`begin_generation`), which records the sealed one as its parent. The\n  sealed file stays on disk byte-for-byte, so the whole conversation is\n  recoverable by walking `session_chain` even though the model reads only the\n  head.\n- **A resumed session's prompt is frozen.** It reuses its persisted system\n  messages verbatim, which is what keeps the provider's prefix cache warm\n  across a restart. The accepted consequence is that prompt edits, new skills\n  and newly connected integrations do not reach an existing thread, unless\n  the agent's definition sets `refresh_prompt_on_resume`: then a resumed\n  thread is sent the prompt rendered now, and a changed prompt seals the\n  current generation and continues the conversation in the next\n  (`agent/session_host/runtime_session/prompt_refresh.rs`).\n- **So is the tool list it was sent.** Every turn records its tool\n  declarations in the transcript (a `{\"kind\":\"tools\"}` record, written only\n  when they change); resume restores them, the prefix, and the committed-turn\n  count. The host never shrinks a thread's tools because a cache went cold:\n  `session_host/recorded_tools.rs` rebuilds recorded Composio actions as\n  deferred executors, and the prelude fetches integrations on the first turn\n  of every session instance, not only on a brand-new thread. Explicit embed\n  `Turn::tools` overrides and host-only belts are authoritative instead: they\n  do not restore revoked tools from an earlier transcript.\n- **Pre-identity conversations are adopted once**, on first resume, from the\n  timestamped stems they were written to (`adopt_legacy_session_transcripts`).\n  No legacy file is modified.\n- OpenHuman's session host keeps only the product surface over this: start a\n  session, read it back, list its generations\n  (`agent/session_host/session_api.rs`).\n\n`CoreBuilder` controls background services with `ServiceSet`, runtime domains\nwith `DomainSet`, and tool visibility with `ToolGroups`. These controls only\nnarrow capabilities.\n\nCargo default features define the contributor build;\n`scripts/ci/product-features.txt` defines the shipped product. The Tauri shell\ndisables default features, so product gates must be forwarded explicitly on\nits `openhuman-rpc` dependency in `crates/openhuman-app/Cargo.toml` and\nchecked by `scripts/ci/check-feature-forwarding.mjs`. The same gate checks the\nlibrary chain: a core gate must be forwarded by `openhuman-embed`, then\n`openhuman-tinyhumans`, then `openhuman-rpc`, then the `openhuman-cli` and\n`openhuman-tui` hosts (each to `openhuman-rpc/<gate>`), or be listed in\n\n## Storage engine ownership\n\ntinystoragedrivers (`vendor/tinystoragedrivers`) is the only crate family that may\ndepend on a storage engine. Engines (rusqlite, mongodb, aws-sdk-s3, lapin,\nobject_store, sqlx, redb, sled, rocksdb, fs2, and their transitive deps) are\nreachable only from the following composition roots, which are the only\npackages listed in the ALLOWLIST in `scripts/ci/check-storage-owner.mjs`:\n\n  openhuman          core storage domain (installs and owns the backend)\n  openhuman-app      keyring secrets via rusqlite\n  openhuman-cli      keyring secrets via rusqlite\n  openhuman-tui      keyring secrets via rusqlite\n  openhuman-rpc      keyring secrets via rusqlite\n  tinychannels       iMessage chat.db reader (read-only SQLite)\n  tinybox-cli        bin target, bundles engine\n  tinyjuice          bin target, bundles engine\n\nEvery other first-party crate must reach tinystoragedrivers only through\n`tinystoragedrivers-types`, `tinystoragedrivers-api`, `tinystoragedrivers-bus`\nor `tinystoragedrivers-busproxy`. The gate `node scripts/ci/check-storage-owner.mjs`\nwalks the normal+build dependency closure of `--all-features` from every\nnon-root first-party package and fails if it reaches an engine crate.\nRun in `pnpm rust:layout`.\n\n`CHAIN_GATES_NOT_FORWARDED` / `CHAIN_LOCAL_GATES` with a reason. Test both enabled and disabled\nbuilds after changing a gate. Use `scripts/assert-shed.sh` or\n`scripts/dep-sim.py` before claiming a dependency reduction.\n\n## Loadable modules and bus contracts\n\nHosts must interface with loadable components through their minimal `*-bus` contracts. Never import, re-export, link, or call their implementation libraries directly, including through wrapper crates or indirect dependencies. Execute component behavior through the compiled TinyBus module. If the required types or operations are missing, extend the contract and implementation in the owning repository and raise a PR against its canonical upstream first; then consume the released module and update OpenHuman’s gitlink and adapter. Do not introduce a host-side implementation workaround.\n\n### Submodule ownership\n\nOpenHuman is the host and orchestrator for these components. It composes them,\nloads modules, adapts their contracts to product RPC/tools, and applies\nOpenHuman-specific configuration, security policy, approvals, and lifecycle\nrules. It is not the implementation home for behavior that belongs to a\nvendored project.\n\nBefore changing code, identify the owning repository below. Implement a\nmodule/library capability, bug fix, or contract change in that submodule,\nraise its PR against that repository's canonical upstream, and update the\nOpenHuman gitlink only after the upstream change is available. OpenHuman may\ncontain the host adapter and integration tests that prove the composition, but\ndo not copy the module implementation into OpenHuman or add a host-side\nworkaround for a defect owned by a submodule. For a change that spans a module\nand its host adapter, make both changes in their respective repositories and\nraise the module PR first. Keep PRs and gitlinks independently reviewable.\n\nDirect rendered submodules under `vendor/`:\n\n| Submodule | Owns |\n| --- | --- |\n| `tiny-artifacts` | Versioned artifacts as a plain library (no bus contract): the record and version model, the `Artifacts` service and its rules (content fits the kind, file paths stay inside the host's `FileRoots` and pass its guard, revisions append, kept version files, publishing), the agent artifact tools, and the file-system store. OpenHuman owns the ports: the document-backed store, the card UI bridge, the protected-path guard and the cloud-storage publisher (`artifacts/`). |\n| `tinyagents` | Provider-neutral agent harness and durable typed state graph: model/tool loop, tool-call dialects and parsing, middleware, retries, caching, sessions/transcripts, and graph execution. |\n| `tinybox` | Isolated execution environments for code the host does not trust; box lifecycle and isolation backends. |\n| `tinybus` | TinyBus runtime and module contracts: discovery/loading, ABI and manifest admission, transport, proxies, lifecycle, and module bus behavior. |\n| `tinychannels` | Portable channel/message contracts, configuration/schema, routing metadata, and channel backend abstractions. OpenHuman owns its concrete product/backend adapters. |\n| `tinyconnectors` | OAuth connector module behavior: account linking, available actions, action execution, and connector webhooks. |\n| `tinycomputer` | Computer use as one TinyBus module: native desktop accessibility observation and interaction, browser sessions and control (the `Browser*` members), and tasks (`StartTask`/`AwaitTask`/`ContinueTask`) driven by a selectable decision model (Jev, OpenJev, Sage) with planner and rescue models. |\n| `tinydocs` | Document extraction and synthesis, including PDF reading and DOCX/PPTX generation. |\n| `tinyflows` | Host-agnostic workflow graph definition, validation, compilation, and execution engine. |\n| `tinyhosts` | Hosting provider APIs and deployment/database/domain/analytics operations, as library and TinyBus module. |\n| `tinyhumans-sdk` | Rust client types and transport operations for the public TinyHumans backend API. OpenHuman owns its transport integration and product auth/session policy. |\n| `tinyjuice` | Agent tool-output compression and recovery of omitted content. |\n| `tinymcp` | The TinyMCP module implementation and its bus contract. Put MCP module behavior and contract changes here; OpenHuman owns configuration, lifecycle, and host integration. |\n| `tinymemory` | Engine-neutral memory contracts, operations, and providers. Its nested TinyCortex submodule owns the TinyCortex memory engine. |\n| `tinystoragedrivers` | Storage ports, drivers (sqlite/file/mongodb/s3), secrets, storage bus contract; every module's tsd dependency is patched onto this checkout. |\n| `tinysearch` | Web-search module, provider dispatch, tool declarations, and execution behind its TinyBus contract. |\n| `tinyskills` | Host-independent skill/workflow bundle parsing, discovery, scope resolution, resource inventory, and safe reads. OpenHuman owns trust and execution policy. |\n| `tinyvoice` | Host-agnostic voice primitives such as audio framing, VAD, wake-word gating, routing, and STT hallucination detection. |\n| `tinywallet` | Multi-chain wallet: `tinywallet-crypto` (address, asset, chain, `rpc::Transport`, tx codec), `tinywallet-x402` (x402 wire, payment, spending ledger, `x402_request` tool), `tinywallet-web3` (wallet engine, per-chain build/sign/broadcast flows, swap/bridge/dapp quotes, agent tools) behind host seams (`WalletSigner`, `PaymentSigner`, `WalletAccounts`, `RpcEndpoints`, `QuoteScope`, `Web3Backend`, `ProxyPolicy`), and the loadable `tinywallet-module` that derives keys and signs. OpenHuman keeps keyring, consent, credentials, config, controllers and the seam impls under `web3/`. |\n\nSome rendered submodules are shared dependencies nested inside those projects,\nnot separate OpenHuman feature implementations. Make changes to them in their\nown canonical repositories as well:\n\n| Nested submodule | Owns |\n| --- | --- |\n| `tinyagents/vendor/tinytools` | Shared `Tool` trait and generic tool types. This is the single `tinytools` copy used by OpenHuman. |\n| `tinyagents/vendor/tinyinference` and `tinymemory/vendor/tinyinference` | Inference/provider, embedding, local model, and voice inference libraries. OpenHuman patches the TinyAgents copy in its Cargo workspace; do not create a competing copy. |\n| `tinymemory/vendor/tinycortex` | TinyCortex engine implementation for the TinyMemory contracts. |\n| `*/vendor/tinybus` | Shared TinyBus contract/runtime dependency; change the owning TinyBus project, not a vendored duplicate. |\n| `tinycomputer/vendor/agent-browser` | Browser-control library used by TinyComputer. |\n| `tinycomputer/vendor/agent-desktop` | Cross-platform desktop accessibility and interaction library used by TinyComputer. |\n| `*/vendor/tinyjevclient` | Shared TinyJEV client used by the browser and desktop modules. |\n| `tinyagents/wiki`, `tinychannels/wiki`, `tinyjuice/wiki` | Project documentation content, not runtime implementation. |\n\nWhen ownership is unclear, inspect the submodule's README, crate boundaries,\nand bus contract before editing. A behavior change belongs with the code that\ndefines that behavior; OpenHuman changes should be limited to the host-side\ncomposition and policy described above.\n\nEach loadable module has a small `*-bus` contract crate for interface names,\nmethod constants, request and response types, and its contract version.\n\n| Contract | Feature or role |\n| --- | --- |\n| `tinydocs-bus` | `documents` |\n| `tinyvoice-bus` | `voice` |\n| `tinyjuice-bus` | inference kernel |\n| `tinywallet-bus` | `web3` (contract; the chain primitives are in `tinywallet-crypto`) |\n| `tinymcp-bus` | `mcp` |\n| `tinychannels-bus` | channel vocabulary |\n| `tinyconnectors-bus` | OAuth connector (Composio) wire contract; called through `integrations/composio/module_client.rs` |\n\nRules:\n\n- Never redeclare a contract type in OpenHuman.\n- Call members through contract constants, not string literals.\n- Contract crates stay synchronous and free of I/O and runtime dependencies.\n- Shared wire vocabulary belongs in the contract. Component algorithms execute\n  in the compiled module. Runtime, config, and security\n  policy stay in the host.\n- Test the handwritten registry metadata against each contract's bus name and\n  object path.\n- Initialize recursive submodules before building: `git submodule update\n  --init --recursive vendor/`.\n\nNative modules are first-party `cdylib` files loaded into the core process.\nThey share its privileges and crash domain.\n\n- Only the compiled registry may select artifacts.\n- Pin release checksums from the published release. Do not compute replacement\n  pins from a local build.\n- Keep ABI, manifest, dependency, and digest admission checks.\n- Do not unload or repeatedly retry a faulted module in the same process.\n- Untrusted code belongs in a separate process.\n- Do not enable the `modules` feature directly on the unconditional\n  `tinybus` dependency. Forward it from OpenHuman's own feature.\n\nModule sources (`OPENHUMAN_MODULE_SOURCE`, `[modules] source`; details in\n`crates/openhuman-core/src/modules/README.md`, \"Module source\"):\n\n- `release` is the registry-pinned, digest-checked GitHub artifact. CI and\n  shipped builds use `release` only; never document or configure `linked` or\n  `source` for CI or production.\n- `linked` (in-process factory) and `source` (a locally built artifact from the\n  gitlink) are local development and local tests only.\n- Iterate with `scripts/modules/build.mjs`, check the pin with\n  `scripts/modules/pin-status.mjs` before pushing, and release and re-pin with\n  `scripts/modules/release-and-repin.mjs`.\n\n`[components]` (`config.toml`, `OPENHUMAN_COMPONENT_<NAME>=0|1`) holds one bool\nper tiny dependency, all default true. It resolves to a `ComponentSet` that only\nnarrows the host's `DomainSet`, `ServiceSet` and `ToolGroups`, and takes effect\non the next core start. It adds no Cargo gate. A disabled component's module is\nnever downloaded, mapped or registered (`ModuleState::Disabled`,\n`LoadError::Disabled`), its tools are not registered, its RPC namespace answers\n`COMPONENT_DISABLED:<name>`, and `about_app` marks its capabilities `disabled`.\nSee `docs/gitbooks/en/developing/components.md`.\n\nMemory uses `tinymemory-api` as its contract. `memory::api` is a selective\nre-export of the wire surface, not a place to copy or widen the whole crate.\nPass source scope and self-echo exclusions explicitly because task-local state\ndoes not cross a module boundary. Confirm that a method exists in the pinned\nmodule release before migrating a host call to it.\n\n## Backend API\n\nThe core has no backend module, no hosted port, no hosted route and no\nTinyHumans hostname (`crates/openhuman-core/src/backend/` is gone, and\n`node scripts/ci/check-core-hosted-free.mjs`, part of `pnpm rust:layout`, fails\non `crate::backend`, `tinyhumans.ai` and the hosted route literals\n`agent-integrations`, `/voice-agent`, `/openai/v1`, `/telemetry/langfuse`,\n`/memory/free-period` and `/teams/me` in core source outside comments and\n`_tests.rs`). The core does not depend on `tinyhumans-sdk`; the only crate that\nmay is `openhuman-tinyhumans` (`cargo tree -p openhuman -i tinyhumans-sdk` must\nstay empty).\n\n`crates/openhuman-tinyhumans` owns everything hosted: the transport port\n(`BackendTransport`, `BackendRequest`, `BackendTransportError`,\n`TransportProfile`, `BaseUrlPurpose` in `src/transport/port.rs`) and its\nSDK-backed implementation (`SdkBackendTransport`), the authenticated client\n(`src/client/`: `BackendClient`, `HostedApiError`, `flatten_authed_error`), the\nURL, header and product-identity policy (`src/backend/`: `url.rs`, `headers.rs`,\n`product.rs`), the hosted routes and RPC proxies (`src/hosted/`), and the hosted\nproviders (`src/integrations/`, `src/voice/`, `src/inference/`,\n`src/channels/`, `src/memory/`). It builds one process-wide transport\n(`openhuman_tinyhumans::client::shared_transport`); the core holds no transport\nslot (`CoreBuilder::backend_transport` and the `CoreContext` accessor are\nremoved). What a backend response *means* is decided in the transport\n(`tinyhumans_sdk::classify`, applied by `map_sdk_error`); the recovery lives in\n`BackendClient` and `IntegrationClient::map_transport_error`\n(`crates/openhuman-tinyhumans/src/integrations/client/errors.rs`). Add missing\nbackend routes to the vendored SDK (its unexposed-route registry is the route\npolicy the transport enforces), not to the core.\n\nTinyhumans plugs into the core only through generic registries, keyed by a\nslug or id, that know nothing about TinyHumans and that any other host could\nfill. `openhuman_tinyhumans::install` (or `RuntimeBuilder::connect`) registers\nthem through `register_all` (`src/register.rs`), reaching the core via\n`openhuman_embed::__host`:\n\n| Hook | Core module path |\n| --- | --- |\n| Agent tool sources (media, file storage, integrations, google places, stock prices, web3) | `tools::sources` (`register_tool_source`) |\n| STT, TTS and viseme providers by slug | `voice::factory::registry` |\n| Live voice sessions by slug | `voice::live::registry` |\n| Chat models, embeddings and model catalogs by provider id | `inference::registry` |\n| Channel callback (`ChannelBackendRequest` to `ChannelBackendResult`) | `channels::callback_slot` |\n| Config enricher filling `Config.resolved_endpoints` | `config::register_enricher` (`config/enrichers.rs`) |\n| Memory engines by id and the free-background-work predicate | `memory::engine_registry` |\n| Composio route builder by mode | `integrations::composio::client::register_composio_route_builder` |\n| Connector proxy target by mode | `modules::connectors::register_proxy_target` |\n| Wallet backend handler | `modules::wallet_backend` (`register_wallet_backend`) |\n| Hosted RPC proxies (`billing`, `team`, `referral`, `announcements`, `webhooks`, `channel_link`, `oauth`) | controller extension via `core::all::register_controller_extension` (`DomainGroup::Hosted`) |\n\nRegistrations bind per process, or per `CoreContext` through\n`CoreBuilder::extensions` (the `*_in` variants). A core with nothing registered\nruns agents, memory, tools and RPC without any TinyHumans connection; hosted\nsurfaces are missing and backend-touching calls answer `BACKEND_UNAVAILABLE:`\n(the generic sentinel prefixes stay in `core::observability`). The core reads\nresolved URLs, the socket URL, the tracing ingestion URL and headers, the\nattribution headers and the product identity from `Config.resolved_endpoints`,\nwhich the tinyhumans enricher fills on every config load; it never asks a\ntransport. `Config.api_url` is honoured as an override exactly as before.\n\nEvery host that boots a core (`crates/openhuman-app/src/core_process.rs` and\n`lib.rs::run_core_from_args`, `crates/openhuman-tui/src/runner.rs`,\n`crates/openhuman-cli/src/main.rs`) does it through an `openhuman_rpc::host`\nentry, which connects the TinyHumans layer\n(`openhuman_tinyhumans::RuntimeBuilder::connect`); library hosts call\n`openhuman_tinyhumans::install` or the `RuntimeBuilder` directly. Tests that\nboot the core in-process and reach the mock backend call\n`tinyhumans_boot::boot()`. New backend-only proxy domains belong in\n`crates/openhuman-tinyhumans/src/hosted/`, not in the core.\n\n**Hosted memory.** The `tinyhumans` memory engine is registered through\n`memory::engine_registry` by `crates/openhuman-tinyhumans/src/memory/`. It\nbuilds TinyMemory's CortexDB engine with `EngineSettings { endpoint, headers,\nscope_root, .. }` and `EngineCredential::Dynamic(HostBearer)`, and TinyMemory's\nown `reqwest` client (`tinymemory-integrations/src/cortex/transport`) calls the\nbackend's memory routes directly: none of those requests pass through\n`SdkBackendTransport`, so transport-level policy (the route registry,\n`map_sdk_error`) does not apply to them. The credential comes from\n`resolve_backend_credential` on every request. The `cortexdb` engine likewise\ncalls CortexDB directly with the user's key.\n\nEvery TinyHumans backend request must carry a sanitized `x-sdk-name`, stamped by\nthe tinyhumans attribution headers (`src/backend/headers.rs`):\n\n- `BackendClient`\n- `IntegrationClient`, except redirected file downloads\n- the host session owner's login-token consume and current-user requests\n  (`openhuman_tinyhumans::session`, through `ClientHeaders`)\n- the agent's Langfuse and OTLP ingestion push\n- the managed model catalog listing\n\nSet `ProductIdentity` once during startup before building clients. Do not add\nthis header to third-party endpoints, MCP servers, BYOK inference endpoints, or\npresigned storage redirects.\n\nSearch for `bearer_authorization_value` and `header(AUTHORIZATION` when\nauditing hand-built backend requests.\n\n## Event bus\n\n`crates/openhuman-core/src/core/bus.rs` owns the process-wide `BUS` singleton. Use `BUS.publish` and\n`BUS.subscribe` for domain events. Use `BUS.native()` for typed, in-process\nrequest and response calls that carry values which cannot cross a serialized\ntransport.\n\nEach subscribing domain owns a `bus.rs`. Subscriber names use\n`<domain>::<purpose>`.\n\nWhen adding an event:\n\n1. Add it to `DomainEvent`.\n2. Extend the `domain()` match.\n3. Register its subscriber at startup.\n4. Bump `EVENTS_VERSION` in `crates/openhuman-core/src/core/bus.rs`.\n\nNative r\n\n<!-- File: .claude-plugin/marketplace.json (Tokens: ~300 | Category: marketplace) -->\n# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/marketplace.json)\n\n<!-- File: .claude-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.claude-plugin/plugin.json)\n\n<!-- File: .cursor/mcp.json (Tokens: ~300 | Category: mcp-config) -->\n# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/tinyhumansai/openhuman/HEAD/.cursor/mcp.json)\n</yakaai_skills>"}