> For the complete documentation index, see [llms.txt](https://tinyhumans.gitbook.io/openhuman/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tinyhumans.gitbook.io/openhuman/zh/kai-fa/architecture/frontend.md).

# 前端

\`app/src/\` 中的 React 和 Vite 前端：状态、服务、提供方、路由、组件和 hooks。

`app/src/` 是 OpenHuman 桌面 UI：pnpm 工作区中的一个 Vite 和 React 19 代码树 `openhuman-app`。它使用带持久化的 Redux Toolkit 管理会话状态，通过 JSON-RPC 与进程内 Rust 核心通信（`coreRpcClient` 通过本地 HTTP，并借助 Tauri `relay_http_rpc` 命令作为回退中继）以及 socket.io（`socketService`），并通过 REST 访问云后端（`apiClient`）。重逻辑都在核心中，不在这里。

本页是单页参考。请使用目录在各部分之间跳转。

这个代码树里还包含一个移动端壳： `AppRoutesIOS.tsx`, `pages/ios/`，以及 `services/transport/` 连接配置，以及 `app/src-tauri-mobile/` 宿主。该客户端仍属实验性质，不属于已发布的桌面宿主，而后者仅面向 Windows、macOS 和 Linux。之所以在这里出现，是因为这些文件位于同一代码树中，并共享 `App.tsx` 提供者链。请参见 [iOS 配套端](/openhuman/zh/gong-neng/ios-companion.md) ，了解它是什么，以及哪些内容仍没有桌面端界面。

## 快速参考

| 部分                            | 涵盖内容                                                            |
| ----------------------------- | --------------------------------------------------------------- |
| [架构](#architecture-overview)  | 提供者链、构建、布局、约定                                                   |
| [状态管理](#state-management)     | Redux Toolkit 切片、选择器、持久化                                        |
| [服务层](#services-layer)        | `apiClient`, `socketService`, `coreRpcClient`                   |
| [提供者](#providers)             | `ThemeProvider`, `CoreState`, `Socket`, `ChatRuntime` providers |
| [页面与路由](#pages-and-routing)   | `HashRouter`、路由守卫、主路由                                           |
| [组件](#components)             | UI / 设置组件模式                                                     |
| [钩子与工具](#hooks-and-utilities) | 共享钩子、辅助函数、配置                                                    |

## 规模

| 指标                                      | 如何阅读                                                  |
| --------------------------------------- | ----------------------------------------------------- |
| 位于以下目录下的 TypeScript / TSX 文件 `app/src/` | `find app/src -name '*.ts' -o -name '*.tsx' \| wc -l` |
| 应用级钩子                                   | `ls app/src/hooks/*.ts app/src/hooks/*.tsx \| wc -l`  |
| 测试运行器                                   | Vitest（`app/test/vitest.config.ts`)                   |

## 目录结构

```
app/src/
├── App.tsx                 # 提供者链 + HashRouter 壳（桌面 + 移动端壳）
├── AppRoutes.tsx           # 桌面路由表（移动端使用 AppRoutesIOS.tsx）
├── main.tsx                # 入口（polyfills、Sentry、store、样式）
├── store/                  # Redux 切片、选择器、userScopedStorage 持久化
├── providers/              # ThemeProvider、CoreStateProvider、SocketProvider、ChatRuntimeProvider
├── services/               # apiClient、socketService、coreRpcClient、transport/、api/*
├── lib/                    # AI 提示词加载器、i18n、MCP 辅助、平台、隧道加密
├── pages/                  # 路由级页面（包括 onboarding/、ios/、dev/）
├── features/               # 功能垂直模块（human/、conversations/、voice/、wallet/、skills/）
├── components/             # 共享 UI（包括 settings/、layout/shell/、accounts/）
├── hooks/                  # 应用钩子
├── utils/                  # 配置、Tauri 命令包装、路由工具
└── assets/                 # 图标和静态资源
```

## 架构概览

### 系统架构

OpenHuman 的桌面 UI 是一个 React 19 应用（`app/src/`），它：

* 使用带持久化的 Redux Toolkit 管理与会话相关的状态
* 通过 REST 连接后端（`apiClient`）并通过 Socket.io 连接本地核心（`socketService` → 核心 socket 端点）
* 通过 HTTP 调用 Rust 核心（作为 tokio 任务嵌入在 Tauri 宿主中），借助 `coreRpcClient` （JSON-RPC 方法实现于 `crates/openhuman-core/src/`）；非回环的明文 HTTP 运行时会通过 Tauri `relay_http_rpc` 命令中继
* 将 AI 提示词留给核心处理：打包的 `crates/openhuman-core/src/agent/prompts` 作为 Tauri 资源发布（见 `crates/openhuman-app/tauri.conf.json` 资源），并由核心端读取，而不是前端
* 在以下目录下使用一个精简的类 MCP 辅助层： `lib/mcp/` （传输、校验）

聊天消息使用核心的类型化内容契约。附件上传由核心接收，核心负责存储它们，并为消息返回不透明引用；前端不会将本地路径或内联媒体字节作为会话内容持久化。核心会在受信任的轮次上下文中解析这些引用，然后再调用模型。Web 聊天、TUI 和经由中继的 Web 进度共享 TinyChannels 编译后的 web-output 投影；provider 交付则保留其独立的通道终止事件。TinyChannels v2 负责规范的类型化 web 事件模式和投影，而 TinyAgents 负责通用聊天轮次接入和队列机制。OpenHuman 的核心将二者适配到其 RPC、socket、会话、审批和进程事件总线边界；前端消费由此产生的事件契约，不实现 provider 或 agent-runner 逻辑。

### 入口点

| 文件                      | 用途                                                                       |
| ----------------------- | ------------------------------------------------------------------------ |
| `app/src/main.tsx`      | React 根节点、polyfills、Sentry 边界、store、全局样式                                 |
| `app/src/App.tsx`       | 提供者链（见下文）+ 桌面/移动端壳、设置                                                    |
| `app/src/AppRoutes.tsx` | `HashRouter` 路由、 `ProtectedRoute` / `PublicRoute` / `DefaultRedirect` 守卫 |

### 提供者链

*生成自 `app/src/App.tsx` 由 `scripts/generate-architecture-docs.mjs`。请勿手动编辑——运行 `pnpm docs:generate` 以刷新。*

| #  | 组件                     | 角色                                               |
| -- | ---------------------- | ------------------------------------------------ |
| 1  | `Sentry.ErrorBoundary` | 崩溃边界；渲染 ErrorFallbackScreen                      |
| 2  | `Provider`             | Redux store；使 useAppSelector / dispatch 可在全局使用   |
| 3  | `PersistGate`          | 在持久化的 Redux 切片重新水合前阻止 UI 渲染                      |
| 4  | `ThemeProvider`        | 主题令牌和深色模式处理                                      |
| 5  | `I18nProvider`         | 通过 useT 消费的本地化上下文                                |
| 6  | `BootCheckGate`        | 在核心启动快照解析完成前阻止渲染                                 |
| 7  | `CoreStateProvider`    | 核心应用快照：认证、会话、引导状态                                |
| 8  | `SocketProvider`       | 核心 socket.io 事件；仅桌面端使用（移动端使用 TunnelTransport 中继） |
| 9  | `ChatRuntimeProvider`  | 聊天运行时事件、工具时间线和审批                                 |
| 10 | `Router`               | 为所有路由提供 HashRouter 导航                            |
| 11 | `CommandProvider`      | 命令面板上下文                                          |
| 12 | `ServiceBlockingGate`  | 在所需服务配置完成前阻止壳渲染                                  |

为何按此顺序

1. Redux `Provider` 位于最外层，因此 `useAppSelector` ／dispatch 可在任何地方工作。
2. `PersistGate` 在子组件假定认证/会话已稳定前，先重新水合持久化切片。
3. `BootCheckGate` / `CoreStateProvider` 在功能提供者挂载前先解析核心启动快照（认证、引导）。
4. `SocketProvider` （仅桌面端）以及 `ChatRuntimeProvider` 依赖该核心状态来获取实时事件和审批。
5. `Router` 为所有路由提供导航。

### 模块关系（简化）

```
App.tsx
  ├─ Redux store + 持久化器
  ├─ ThemeProvider / I18nProvider - 主题令牌、useT() 本地化
  ├─ BootCheckGate - 等待核心启动快照
  ├─ CoreStateProvider - 认证/会话/引导快照（fetchCoreAppSnapshot RPC）
  ├─ SocketProvider - 连接本地核心的 socket.io（仅桌面端）
  ├─ ChatRuntimeProvider - 聊天流式传输、工具时间线、审批 → Redux
  └─ AppShell（桌面或移动端）
       ├─ AppRoutes - PublicRoute / ProtectedRoute / DefaultRedirect
       └─ SettingsModal - 当 URL 为 /settings/* 时挂载的覆盖层
```

### 服务层（概念）

```
services/
  ├─ apiClient        → 通过 `services/backendUrl#getBackendUrl` 在运行时解析出的 URL 发起 REST 请求
  ├─ backendUrl       → 调用 `openhuman.config_resolve_api_url`；仅在 Tauri 之外才回退到 VITE_BACKEND_URL
  ├─ socketService    → 连接本地核心的 Socket.io（基础 URL 由 RPC URL 派生）；MCP 风格信封
  ├─ coreRpcClient    → 通过 HTTP 向本地 openhuman 核心发起 JSON-RPC；对非回环 http 使用 `relay_http_rpc` 回退
  └─ transport/       → 用于 iOS/远程的 ConnectionProfile 传输（LanHttp、Tunnel、CloudHttp）
```

#### 运行时配置优先级

桌面应用不会将核心 RPC URL 或 API 主机作为硬性要求写死进构建产物中。运行时会按以下顺序解析它们（优先级从高到低）：

1. 欢迎页上的 RPC URL 输入框，通过以下方式保存： `utils/configPersistence` 并在下次启动时恢复。最终用户在这里配置自托管核心地址，而不是手动编辑 `config.toml` 或 `.env` 文件。
2. Tauri `core_rpc_url` 命令中指定的端口，即嵌入式核心为该进程监听的端口。
3. `VITE_OPENHUMAN_CORE_RPC_URL`，用于开发环境的构建时回退。
4. 硬编码的 `http://127.0.0.1:7788/rpc` 默认值。

一旦 RPC 握手成功， `services/backendUrl` 会调用 `openhuman.config_resolve_api_url` 以获取 `api_url` （以及其他安全的客户端字段）从已加载的核心 `Config`. `VITE_BACKEND_URL` 仅在应用运行于 Tauri 之外时作为 Web 回退使用。

需要后端 URL 的组件应调用 `useBackendUrl()` （或者 `getBackendUrl()` （在非 React 代码中），它们不得导入静态的 `BACKEND_URL` 常量，来自 `utils/config`，它仅代表构建时的值。

### 相关文档

* Rust 架构： [架构](/openhuman/zh/kai-fa/architecture/architecture.md)
* Tauri 壳： [Tauri 壳](/openhuman/zh/kai-fa/architecture/tauri-shell.md)

## 状态管理

应用使用 Redux Toolkit 和 Redux-Persist。没有单一的根持久化配置：每个需要持久化的切片都会用其自己的 reducer 包裹 `persistReducer` 在 `store/index.ts`，并精确白名单化应在重启后保留的字段。

### 存储后端

* `userScopedStorage` (`store/userScopedStorage.ts`）是持久化切片的默认存储。数据块的键格式为 `${userId}:persist:<key>` 因此在登出或登录时，状态绝不会泄漏到其他用户。
* 普通 `localStorage` 仅用于登录前、全设备范围的切片（`coreMode`, `locale`, `theme`），这些切片必须在切换用户时仍然保留。

### 切片

权威列表 = `reducer` 映射于 `store/index.ts`。单行用途：

| 切片                    | 用途                                              | 是否持久化？                                                       |
| --------------------- | ----------------------------------------------- | ------------------------------------------------------------ |
| `accounts`            | 已连接的 Web 应用账户 + 轨道顺序                            | `accounts`, `顺序`, `lastActiveAccountId` （不是当前活动 id）          |
| `announcement`        | Harness 初始化公告横幅、已见 id                           | `shownIds`                                                   |
| `channelConnections`  | 消息通道连接（WhatsApp、Slack 等）                        | 连接 + 迁移/默认频道字段                                               |
| `chatRuntime`         | 流式缓冲区、工具时间线、推理状态、产物                             | 仅 `artifactsByThread` （就绪快照）                                 |
| `connectivity`        | navigator.onLine、核心健康状态、渲染进程到核心的 socket、核心到托管链接 | 否                                                            |
| `coreMode`            | 登录前核心模式选择（内嵌 / 自托管 / 云）                         | `mode` （普通 localStorage）                                     |
| `followupSuggestions` | 每个线程最新已定稿轮次的后续建议标签                              | 否（仅内存中）                                                      |
| `githubStar`          | 用户是否关闭了应用内“在 GitHub 上给我们点星”号召行动                 | `dismissed`                                                  |
| `layout`              | 双栏布局几何（侧边栏可见性、拖拽宽度）                             | `panels`                                                     |
| `locale`              | 界面语言                                            | `current` （普通 localStorage）                                  |
| `mascot`              | 吉祥物外观 / 声音选择                                    | `color`, `voiceId`, `customMascotGifUrl`, `selectedMascotId` |
| `notifications`       | 通知条目 + 偏好设置                                     | `items`, `preferences`                                       |
| `persona`             | 外观 persona 的显示名称 + 描述（SOUL.md 位于核心中）            | `displayName`, `description`                                 |
| `queue`               | 核心中按线程划分的运行队列，以及 composer 待处理的后续消息              | 否（仅内存中）                                                      |
| `runMode`             | 按线程的计划/构建运行模式                                   | 否（仅内存中）                                                      |
| `socket`              | 按用户的 socket 连接状态 / socket id                    | 否（启动时重新连接）                                                   |
| `theme`               | 主题模式、字体大小、消息视图模式、自定义主题                          | 普通 localStorage                                              |
| `thread`              | 聊天线程列表 + 每线程消息缓存                                | 仅 `selectedThreadId`                                         |
| `threadGoal`          | 持久的按线程目标状态                                      | 否（仅内存中）                                                      |
| `threadTodos`         | 实时按线程待办列表                                       | 否（仅内存中）                                                      |
| `userErrors`          | 用户可处理的运行时错误                                     | 否（仅内存中）                                                      |
| `walletPreferences`   | 钱包视图的隐藏代币偏好                                     | `hiddenTokenKeys`                                            |

临时聊天状态（流式缓冲区、工具时间线）不能在重启后保留：UI 会尝试恢复一个其实时驱动已消失的轮次。唯一例外是 agent 生成的产物，它们会经过 `artifactsReadyOnlyTransform` 在 `store/index.ts` （纯逻辑位于 `store/artifactsPersistFilter.ts`).

### 类型化 hooks

文件： `store/hooks.ts`

```typescript
// 使用这些，而不是直接使用 useDispatch/useSelector
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
```

### 最佳实践

1. 始终使用类型化 hooks， `useAppDispatch` 以及 `useAppSelector`.
2. 派生状态请使用选择器：见 `store/socketSelectors.ts`, `store/connectivitySelectors.ts`, `store/userErrorsSelectors.ts`.
3. 按 slice 白名单持久化。绝不要持久化瞬态状态或加载状态；为每个 slice 添加一个 `persistReducer` 在 `store/index.ts`.
4. 优先使用 Redux，而不是临时的 `localStorage`。纯 localStorage 仅保留给上面提到的登录前 slices。
5. 在开发和 E2E 构建中，store 以以下形式暴露： `window.__OPENHUMAN_STORE__` 这样 WDIO 规范可以断言底层状态；生产包不会暴露它。

***

## 服务层

应用使用单例服务进行外部通信。这可以防止连接泄漏，并提供一致的 API 访问。

### 服务架构

```
app/src/services/
  ├─ apiClient（HTTP REST）
  │   └─ 后端 URL 在运行时解析（services/backendUrl）
  ├─ socketService（Socket.io）
  │   └─ 连接到本地 core 的 socket 端点（base 来自 RPC URL）
  ├─ coreRpcClient.ts
  │   ├─ 直接 webview fetch → 本地 openhuman core（通过 HTTP 的 JSON-RPC）
  │   └─ 对于非 loopback 的纯 http 运行时，使用 invoke('relay_http_rpc', …) 作为回退
  ├─ coreCommandClient.ts - core RPC 方法上的类型化封装
  ├─ transport/ - 用于 iOS/远程的 ConnectionProfile transports（LanHttp、Tunnel、CloudHttp）
  └─ services/api/* - 领域 API 模块（约 50 个文件，见下）
```

### API 客户端（`services/apiClient.ts`)

基于 Fetch 的 HTTP REST 客户端，用于后端通信，包含类型化的请求/响应处理和错误处理。后端 URL 在运行时解析（`services/backendUrl`），而不是硬编码。

```typescript
import apiClient from "../services/apiClient";

const user = await apiClient.get<User>("/users/me");
const result = await apiClient.post<LoginResponse>("/auth/login", {
  email,
  password,
});
```

### 领域 API 模块（`services/api/`)

约 55 个按领域划分的模块，每个模块对应一个功能面，分别封装后端 REST 端点或 core RPC 方法。代表性示例：

* `authApi` / `userApi`：认证 + 用户资料
* `threadApi`, `threadUsageApi`：聊天线程
* `agentWorkApi`, `subagentApi`：代理
* `skillsApi`, `skillRegistryApi`, `flowsApi`, `workflowRunsApi`：技能与自动化
* `channelConnectionsApi`, `mcpClientsApi`, `mcpSetupApi`, `tunnelsApi`：连接
* `memoryApi`：Memory v2 RPC 封装（`openhuman.memory_*`)
* `billingApi`, `creditsApi`, `referralApi`, `inviteApi`：商业
* `voiceSettingsApi`, `aiSettingsApi`, `modelCouncilApi`：AI/语音配置

完整列表请查看， `ls app/src/services/api/`。新增功能面会在这里拥有自己的模块，而不是继续增长 `apiClient`.

### Socket 服务（`services/socketService.ts`)

连接到本地 core 的 socket 端点的 Socket.io 客户端单例（base URL 通过 `coreSocket.ts`解析后的 RPC URL 派生而来；使用 core RPC token 进行身份验证）。它接收实时 core 事件（连接状态、频道更新）并将其派发到 Redux（`socketSlice`, `connectivitySlice`, `channelConnectionsSlice`）。它还承载 MCP 风格的 transport（`SocketIOMCPTransportImpl` 来自 `lib/mcp`).

）。 `socketService` 请保持 `以及 core socket 行为一致（AGENTS.md 中的“双 socket 同步”规则）。连接生命周期由`providers/SocketProvider.tsx `；在移动端则根本不会挂载该 provider：事件通过` TunnelTransport

### Core RPC（`services/coreRpcClient.ts`)

Rust core 以内嵌方式运行在 Tauri 宿主进程中（没有 sidecar）。UI 通过本地 HTTP 在其上调用 JSON-RPC 方法：

```typescript
import { callCoreRpc } from "../services/coreRpcClient";

const result = await callCoreRpc<MyType>({
  method: "openhuman.some_method",
  params: {
    /* … */
  },
  timeoutMs: 60_000, // 每次调用可选覆盖（默认 30 秒）
  suppressAuthExpiredEvent: false, // 窄读可以选择不在 401 时触发全局登出
});
```

一次调用的流程：

1. URL + token 解析：RPC URL 遵循 [运行时配置优先级](#runtime-config-precedence)中的优先级；每次启动的 bearer token 来自 Tauri `core_rpc_token` 命令（或自托管 core 的已存储 token）。
2. 直接 fetch：webview `fetch()`会把 JSON-RPC 信封直接发送到 core（loopback http 或任何 https URL）。
3. Shell relay 回退：对非 loopback 主机的纯 `http://` 属于活动的 mixed content，Chromium 会阻止它。 `rpcUrlNeedsShellRelay()` 会检测到这一点，并将调用路由到 `invoke('relay_http_rpc', { url, token, body })`，实现于 `crates/openhuman-app/src/core_rpc.rs` （对 `openhuman_rpc::post_json_rpc` 来自 `crates/openhuman-rpc/`的一个薄包装），它返回 `{ status, body }` 并被重新包装为一个 `Response`.
4. 传输覆盖：iOS/远程连接配置会安装一个 `CoreTransport` (`setActiveCoreTransport`），因此同一个 `callCoreRpc` 接口可以使用 LAN/隧道/云端传输。

错误会被分类为稳定的 `CoreRpcError.kind` (`auth_expired`, `transport`, `timeout`, `rate_limited`，…）：调用方根据 `kind`分支，而绝不根据消息正则表达式分支。一个 `auth_expired` 分类会广播 `core-rpc-auth-expired`，这会 `CoreStateProvider` 转变为会话清除。

### 最佳实践

1. 使用单例。绝不要创建多个服务实例。
2. 将 Tauri IPC 和 RPC 调用放在服务中。不要把 `invoke()` 或原始 fetch 分散到组件中。
3. 在卸载时通过在 `useEffect` 清理函数中断开连接来清理。
4. 通过 `CoreRpcError.kind` 处理错误，并且只重试瞬态故障。

***

## 提供者

React context providers（`app/src/providers/`）管理服务生命周期并暴露由 core 拥有的状态。完整的嵌套（包括位于 `components/`中的 gate）就是上面的生成 [provider 链](#provider-chain) 。没有 `UserProvider`, `AIProvider` 或 `SkillProvider`。认证和用户状态存在于 `CoreStateProvider`，AI 配置存在于 Rust core 中，而技能在 core 中运行。

### ThemeProvider（`providers/ThemeProvider.tsx`)

应用来自持久化 `theme` slice（模式、字体大小、自定义主题）的主题 tokens 和深色模式处理。

### CoreStateProvider（`providers/CoreStateProvider.tsx`)

权威的认证/会话/引导上下文。获取 core 应用快照（`fetchCoreAppSnapshot()` RPC），通过 `useCoreState()` (`{ snapshot, isBootstrapping, refresh }`暴露它，并在全局 `core-rpc-auth-expired` 事件上清除会话。它遵循一个 turn 边界重新获取契约：每次代理回复完成后（`chat_done` 在 `ChatRuntimeProvider`）都会重新获取用户状态（750ms 去抖），并通过 `patchSnapshot`合并到快照中：见 `providers/README.md`.

### SocketProvider（`以及 core socket 行为一致（AGENTS.md 中的“双 socket 同步”规则）。连接生命周期由`)

负责与本地 core 的 socket.io 连接：在 core state 就绪后连接一次，更新 `socket` slice，并在卸载时拆除。仅桌面端： `App.tsx` 在移动端跳过它，因为事件通过 `；在移动端则根本不会挂载该 provider：事件通过` relay

### ChatRuntimeProvider（`providers/ChatRuntimeProvider.tsx`)

订阅聊天运行时 socket 事件（消息流、工具调用、子代理生命周期、审批请求），并将其归约到 `chatRuntime` slice 中：按线程的工具时间线、流式缓冲区、制品以及审批状态，供聊天界面和吉祥物使用。

### 门控和 shell 级上下文（位于 `components/`)

* `BootCheckGate` (`components/BootCheckGate/`）会阻止渲染，直到 core 启动快照解析完成。
* `CommandProvider` (`components/commands/`）保存命令面板上下文。
* `ServiceBlockingGate` (`components/daemon/`）会阻止 shell，直到所需服务被配置完成。

### Context 与 Redux

| Context 适用于         | Redux 适用于     |
| ------------------- | ------------- |
| 服务实例（socket、client） | 可序列化状态（状态、数据） |
| 方法（emit、on、off）     | 持久化状态（会话、令牌）  |
| 派生值                 | 复杂状态逻辑        |

示例： `SocketProvider` 持有 socket 实例；Redux 将连接状态存储在 `socketSlice`.

***

## 人类吉祥物界面

吉祥物会故意出现在两个界面上。 `/human` (`app/src/features/human/HumanPage.tsx`）是专用的全屏舞台，右侧有聊天栏。 `/chat` 在其 composer 上携带同一个吉祥物，并在原位展开为语音舞台。两者都从 `mascotSlice` 读取同一组吉祥物偏好（颜色、语音、朗读回复、关闭），因此两边永远不会对同一个设置产生分歧。

`app/src/features/human/chatMascot/` 负责聊天侧界面：

| 模块                      | 角色                                                                   |
| ----------------------- | -------------------------------------------------------------------- |
| `ChatMascotContext.tsx` | 共享的 dock/stage 引用和发送绑定。每个值都是稳定的：见下面的重渲染说明。                           |
| `ChatMascotDock.tsx`    | 站在 composer 输入框上的小吉祥物。一个锚点 + 命中区域；它不绘制任何内容。                          |
| `ChatMascotStage.tsx`   | 放大的语音界面： `MicComposer`、输入设备选择器、朗读回复开关、折叠按钮。                          |
| `ChatMascotOverlay.tsx` | 单个 Rive 实例，通过一个 `transform`.                                         |
| `geometry.ts`           | 纯粹的 dock ⇄ stage 变换数学（`inscribedSquare`, `lerpBox`, `boxTransform`). |

点击 dock 后，吉祥物会展开为右侧的舞台列，而转录和文本 composer 仍然保留在左侧列中，因此语音和文本属于同一段对话。 `pages/Accounts.tsx` 会为列宽做动画； `ChatMascotOverlay` 每一帧都会重新测量两个锚点，因此吉祥物始终贴合在仍在移动的目标上。展开/折叠以及朗读回复偏好都会持久化到 `mascotSlice`.

这里有两个不变量很重要。吉祥物在 TTS 口型同步期间以约 60fps 重新渲染，因此（a）它被渲染为叶子节点，下面没有任何内容；（b）吉祥物 context 值刻意设计为非响应式：响应式状态保留在 Redux 或发送绑定外部 store 中。响应式 context 值会在每一帧重新协调整个聊天树，从而卡住 UI。而且 overlay 只会在选择了代理账号时挂载，因此固定 overlay 永远不会作为看不见的画布留在另一个账号之下持续消耗帧。

吉祥物脸部来自 `useHumanMascot`，它会订阅聊天生命周期事件中的思考、说话、确认和错误状态，以及由 `监听` 姿态驱动的 `MicComposer`的 `onRecordingChange`.

子代理委派由 `SubMascotLayer`可视化。它不会引入新的 socket 协议。相反，它读取所选或活动线程的 `chatRuntime.toolTimelineByThread` 条目，这些条目 `ChatRuntimeProvider` 已经由 `subagent_spawned`, `subagent_completed`, `subagent_failed`, `subagent_iteration_start`, `subagent_tool_call`以及 `subagent_tool_result`.

生命周期映射：

| 运行时时间线状态 | 子吉祥物状态                 |
| -------- | ---------------------- |
| `运行中`    | 带有短活动气泡、处于思考表情的小型彩色吉祥物 |
| `成功`     | 同一个吉祥物变为开心表情和完成气泡      |
| `错误`     | 同一个吉祥物变为担心表情和失败气泡      |

活动气泡文本故意保持简洁：当前子工具调用、子迭代、委派提示摘录，或最终状态。线程时间线仍然是权威的详细视图；子吉祥物只是围绕主吉祥物的一个可一眼查看的编排层。

### 工具调用展示

所有命名工具调用的界面（聊天卡片、处理面板、状态行、吉祥物）都通过同一个注册表来解析， `app/src/features/conversations/tools/toolPresentation.ts` (`describeToolCall`）。它会返回图标、两个时态的翻译短语（运行中时为“正在读取文件”，稳定后为“已读取文件”）、目标芯片，以及调用会展开成哪个富内容。数据位于 `toolSpecs.ts` （精确名称、会根据参数切换的折叠工具、前缀族、命名代理）以及 `toolPhrases.ts` （短语，作为 `conversations.tools.<id>.active|done`提供）。Composio 动作 slug（`GMAIL_SEND_EMAIL`）通过位于 `components/composio/toolkitMeta.tsx` 中的工具包目录解析为“已使用 Gmail · 发送邮件”，并显示应用图标。服务器端的 `tool_display_label` 仅用于注册表无法描述的工具。

### assistant-ui 组合

对话 UI 使用 `@assistant-ui/react` 用于交互和运行时状态， `@assistant-ui/react-lexical` 用于富文本编辑器，以及 `@assistant-ui/react-markdown` 用于消息渲染。其下的视觉组件 `components/assistant-ui/` 是通过应用的 shadcn 配置（`base-nova`）安装的 assistant-ui 注册表源码，包含 OpenHuman 令牌、翻译和产品插槽。注册表组件是可编辑源码；它们并不是 npm 运行时包中单独导出的组件。

| 表层             | assistant-ui 所有者                                           | OpenHuman 适配器                                                            |
| -------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------ |
| 消息列表、滚动和滚动锚点胶囊 | `ThreadPrimitive.Viewport`, `消息`, `ScrollToBottom`         | 通过 `adapters.threadList.threadId`获得真实线程身份；禁用 run-start 跳转，以便在历史记录中保留阅读位置 |
| 主编辑器、发送和取消     | `ComposerPrimitive`, `LexicalComposerInput`                | 草稿持久化、模型选择、产品语音模式以及由核心支持的文件摄取                                            |
| 消息编辑           | 消息作用域的 `ComposerPrimitive.Root`, `输入`, `发送`, `取消`          | 编辑 RPC 以及关于后续轮次的已翻译警告                                                    |
| 消息操作和分支        | `ActionBarPrimitive`, `BranchPickerPrimitive`              | 适配器能力门控以及由核心支持的重生成、语音和反馈                                                 |
| 排队的后续项         | `ComposerPrimitive.Queue`, `QueueItemPrimitive.Text`, `移除` | 核心运行队列适配器和已翻译的注册表行标题                                                     |
| Markdown、代码和引用 | `MarkdownTextPrimitive` 以及注册表元素                            | 源码投影、应用链接、数学和工件访问                                                        |
| 推理和工具组         | `MessagePrimitive.GroupedParts`，注册表工具组/推理元素                | 核心时序和分组将推理与调用交错在一起                                                       |
| 任务和子代理         | 注册表任务卡/子代理列表， `ReadonlyThreadProvider`                     | 委派状态和等待用户回复                                                              |
| 审批、问题和计划       | 注册表审批卡、引导表单和代理计划                                           | 核心决策 RPC、安全策略以及计划/工作流生命周期                                                |
| 待办事项和目标        | 注册表待办列表和代理状态                                               | 持久化的运行进度和已记住的披露状态                                                        |
| 图片、文档和工具结果     | 注册表图片、工件卡、网页搜索、终端块、代码差异和网页预览                               | 核心工件和工具结果投影                                                              |
| 来源、对话地图和上下文    | 注册表来源、对话地图和上下文元素                                           | 核心来源、搜索、令牌使用量和上下文拆分 RPC                                                  |

将运行时行为保留在库中。新的产品功能应使用运行时适配器或组件插槽，而不是再实现一套滚动控制器、消息存储、编辑器或队列。仅用于展示的注册表组件可以保留在本地；OpenHuman 负责其由 RPC 驱动控件的含义和执行。

渲染使用 assistant-ui 的元素，这些元素被 vendored 到 `app/src/components/assistant-ui/elements/` （tool-call、tool-timeline、web-search、terminal-block、code-diff、web-preview）以及 `tw-shimmer` 工具类。 `ToolGroupRoot` / `ToolGroupTrigger` / `ToolGroupContent` 在工具时间线中将一串调用包装起来； `AssistantUiToolCallCard` 渲染每次调用。位于 `tools/ToolBodies.tsx` 中的适配器只会把工具数据映射到这些元素上。

核心的 `tool_result` 套接字事件携带 `args`, `elapsed_ms`，重新计算后的 `tool_display_label` / `tool_display_detail`以及 `结构化` （该工具的 `ToolResult.metadata`；网页搜索发送 `{ kind: "web_search", query, provider, results: [...] }`). `parseWebSearchResult.ts` 优先使用该负载，并在旧轮次中回退到解析文本渲染。

`tools/__fixtures__/coreToolNames.json` 列出了核心注册的每个工具。Rust 测试 `tools/ops_tests_catalog_fixture_tests.rs` 会保持二者同步（`UPDATE_TOOL_CATALOG=1` 会重新生成它），而且 `toolPresentation.catalog.test.ts` 如果任何列出的工具落入通用回退路径，就会失败，因此新的核心工具不能以未标注的方式进入聊天。 `/dev/tools` （仅开发构建）渲染所有状态和整个目录。

***

## 页面与路由

应用使用 HashRouter，并带有受保护和公开路由守卫。桌面路由位于 `app/src/AppRoutes.tsx`；在移动端（iOS/Android） `AppRoutesIOS.tsx` 则改为渲染精简的 Human/Chat/Settings 集合。

### 路由映射

`app/src/AppRoutes.tsx` 是权威表格，并为每一项都详细注释了原因。它声明的桌面路由：

| 路由                                           | 渲染为                                                                | 守卫                             |
| -------------------------------------------- | ------------------------------------------------------------------ | ------------------------------ |
| `/`                                          | `欢迎`                                                               | `PublicRoute` （已登录：转到 `/home`) |
| `/auth`                                      | `WebCallbackPage`，认证回调                                             | 无                              |
| `/callback/:kind`, `/callback/:kind/:status` | `WebCallbackPage`，通用 OAuth 和提供方回调                                  | 无                              |
| `/onboarding/*`                              | `新手引导` 步进器                                                         | `ProtectedRoute`               |
| `/human`                                     | `HumanPage`，专用的吉祥物舞台                                               | `ProtectedRoute`               |
| `/chat/:threadId?`                           | `账户`，统一聊天（代理加上已连接的网页应用）                                            | `ProtectedRoute`               |
| `/flows`                                     | `FlowsPage`                                                        | `ProtectedRoute`               |
| `/flows/draft`                               | `FlowCanvasDraftPage`，作为 location.state 传入的未保存建议图 `location.state` | `ProtectedRoute`               |
| `/flows/:id`                                 | `FlowCanvasPage`                                                   | `ProtectedRoute`               |
| `/workflows`                                 | `活动`，以及 `SKILL.md` 工作流中心                                           | `ProtectedRoute`               |
| `/workflows/run`                             | `WorkflowsRun`，单一用途的 Skill 运行器                                     | `ProtectedRoute`               |
| `/connections`                               | `技能`，连接中心                                                          | `ProtectedRoute`               |
| `/invites`                                   | `邀请`                                                               | `ProtectedRoute`               |
| `/notifications`                             | `通知`                                                               | `ProtectedRoute`               |
| `/settings/*`                                | `设置`                                                               | `ProtectedRoute`               |
| `/dev/agent-insights`                        | `AgentInsightsPreview`                                             | 仅开发环境                          |
| `/dev/ui`                                    | `UiGallery`，当前主题中的每个共享 UI 原语                                       | 仅开发环境                          |
| `/dev/tools`                                 | `ToolCallGallery`，每种 tool-call 状态和整个核心目录                           | 仅开发环境                          |
| `/dev/assistant-ui`                          | 在模拟运行时上运行的上游 assistant-ui 演示                                       | 仅开发环境                          |
| `*`                                          | `DefaultRedirect`                                                  | 无                              |

这四个 `/dev/*` 路由都注册在一个 `IS_DEV` 分支中（`utils/config`），因此 `import.meta.env.DEV` 会在构建时被替换，分支被折叠掉，它们的组件树会完全从生产包中移除。它们是预览，不属于已发布产品的一部分。

记忆是 `/connections`的一种表层，而不是它自己的路由： `?tab=brain&brain=<chip>` 选择其八个芯片之一， `引擎`, `询问`, `探索`, `学习`, `对话`, `大脑`, `后台` 以及 `设置`。该列表位于 `app/src/components/memory/memoryChips.ts` ，该页面位于 `app/src/pages/Memory.tsx`. `memoryChips.ts` 还会重新映射已弃用的名称（`图表`, `目标` 以及 `上下文` 到 `询问`; `文档`, `来源`, `同步` 以及 `历史` 到 `大脑`），并且 `/settings/memory-engine` 会重定向到 `引擎` 芯片。

向后兼容的重定向，全部 `Navigate replace`。 `ForwardSearch` 会把查询字符串复制到目标位置，因此旧的深链接仍会落到正确的子标签页上：

| 从               | 到                                                                      |
| --------------- | ---------------------------------------------------------------------- |
| `/home`         | `/chat`                                                                |
| `/accounts`     | `/chat`                                                                |
| `/brain`        | `/connections?tab=brain` (`BrainRedirect` 会重新映射旧的 `?tab=` 到 `?brain=`) |
| `/skills`       | `/connections` (`ForwardSearch`)                                       |
| `/channels`     | `/connections?tab=messaging`                                           |
| `/activity`     | `/settings/account`                                                    |
| `/intelligence` | `/settings/account`                                                    |
| `/feedback`     | `/settings/feedback`                                                   |
| `/routines`     | `/flows`                                                               |
| `/webhooks`     | `/settings/integrations` (`ForwardSearch`)                             |

没有 `/login` 路由：认证通过欢迎页、 `/auth` 回调和深链接流转。 `/agents` 也不存在，而 Settings 是普通路由而不是覆盖层（见 [设置](#settings)).

### 路由守卫

这三个守卫都会读取 `useCoreState()` （不是 Redux 认证状态）并渲染 `RouteLoadingScreen` ，同时进行引导：

* `ProtectedRoute` (`components/ProtectedRoute.tsx`, `（{ children, requireAuth = true, redirectTo }）`）：没有会话令牌时，导航到 `redirectTo || '/'`。新手引导门控 *不* 在这里完成；AppShellDesktop 中的一个 effect `AppShellDesktop` （App.tsx）会把非新手引导路由强制送回 `/onboarding` ，只要 `onboarding_completed` 为 false，并在完成后让其跳出。
* `PublicRoute` (`components/PublicRoute.tsx`）：会将已登录用户重定向到 `/home` （其会转到 `/chat`).
* `DefaultRedirect` (`components/DefaultRedirect.tsx`）：已登出 → `/`；已登录但新手引导未完成 → `/onboarding`；否则 → `/chat`。等待 `snapshot.currentUser` 以避免登录后的竞争条件。

### 新手引导流程（`pages/onboarding/`)

一个路由化的步进器（`Onboarding.tsx` 在 `OnboardingLayout`):

```
/onboarding/welcome         → WelcomePage
/onboarding/runtime-choice  → RuntimeChoicePage
  ├── cloud  → /chat
  └── custom → /onboarding/custom/inference → voice → oauth → search
               → embeddings → (activity) → vault → /chat
```

每个自定义步骤都提供 Default（让 OpenHuman 管理它）或 Configure（内联控件，或者对尚未嵌入的域名跳转到 Settings 的深链接提示）。页面位于 `pages/onboarding/pages/`；较旧的 Composio、skills 和上下文收集步骤（`pages/onboarding/steps/`）不在默认流程中。完成情况由核心的 `onboarding_completed` 标志跟踪，并由 AppShell 新手引导门控强制执行。在完成新手引导后， `AppWalkthrough` （Joyride）会运行后新手引导导览。

### 设置

Settings 是一个路由化的 `/settings/*` 页面，像其他任何表层一样，在桌面端和 iOS 上都是如此。它不是模态覆盖层。像 `SettingsPanelLayout`, `useSettingsAnimation` 以及 `ProfilePanel` 这样的旧名称都在下面的 [已移除的名称](#removed-names) 中列出。

* `components/settings/settingsRouteRegistry.ts`：每个设置目的地的单一声明式事实来源（id 和路由 slug、i18n 键、分区、侧边栏 `导航组`, `devOnly`, `searchKeywords`）。导航菜单和面包屑都由它派生。
* `components/settings/settingsRouteElements.tsx`：将注册表项映射到面板 `<Route>` 元素，包括重定向（`/settings/memory-engine` 到 Memory `引擎` 芯片）。
* `components/settings/layout/`：双栏外壳。 `SettingsLayout` 将设置导航投射到应用侧边栏的动态区域； `SettingsSidebar` 按……分组条目 `SettingsNavGroup` (`通用`, `外观`, `agentsAutonomy`, `安全`, `数据`, `knowledgeMemory`, `automationIntegrations`, `diagnosticsLogs`，在 `NAV_GROUP_ORDER`); `SettingsSubNav`, `SettingsIndexRedirect`, `SettingsTabbedPage` 以及 `SettingsPanel`，那个单一面板模板，与它们并列。
* `components/settings/panels/`：诸如以下的叶子面板 `AccountPanel`, `AppearancePanel`, `ThemeStudioPanel`, `AgentAccessPanel`, `AutonomyPanel`, `McpServerPanel`, `PrivacyPanel` 以及 `DeveloperOptionsPanel`。添加一个面板意味着新增组件和注册项；导航和面包屑会自动识别它。
* `components/settings/controls/`：每个面板都会组合使用的共享表单基础组件（`SettingsSwitch`, `SettingsRow`, `SettingsSection`, `SettingsSelect`，以及其他），因此布尔值在任何地方都表现为开关。

### HashRouter 与 BrowserRouter

为了桌面端兼容性，应用使用 HashRouter：

```typescript
// App.tsx
import { HashRouter } from "react-router-dom";

// URLs look like: app://localhost/#/home
// Instead of: app://localhost/home
```

为什么使用 HashRouter：

1. Tauri 深度链接可与基于哈希的 URL 配合使用
2. 无需服务器配置
3. 可与 file:// 协议配合使用
4. 可防止直接访问 URL 时出现 404

### 深度链接处理

深度链接会在路由之前处理：

```typescript
// main.tsx
import("./utils/desktopDeepLinkListener").then((m) => {
  m.setupDesktopDeepLinkListener().catch(console.error);
});
```

监听器会拦截 `openhuman://` URL（例如认证交接），通过 Rust 端交换令牌（绕过 CORS），存储会话，并导航到正确路由。参见 `utils/desktopDeepLinkListener.ts`.

***

## 组件

共享 UI 位于 `app/src/components/`；特定功能的 UI 位于 `app/src/features/<vertical>/`。重点：

```
components/
├── ProtectedRoute / PublicRoute / DefaultRedirect   # 路由守卫
├── layout/shell/            # RootShellLayout、AppSidebar、SidebarSlot（双栏应用外壳）
├── settings/                # 设置注册表、布局、面板、控件（见上文）
├── accounts/                # 已连接应用提供方图标 
├── BootCheckGate/、daemon/  # 提供者链中的启动 + 服务门控
├── commands/                # CommandProvider（命令面板）
├── Announcement/、upsell/、notices/、walkthrough/    # 外壳级覆盖层
├── keyring/、InitProgressScreen/                     # 同意 + 初始化覆盖层
├── memory/                  # Memory v2 选项卡（Engine、Ask、Explorer、Learnings、Conversations、Brain、Background、Settings）和导入横幅
└── intelligence/            # 共享智能 UI（WorkflowsTab、Toast、ConfirmationModal）
```

约定：

* 模态框通过 portal 渲染，位于路由内容之上。设置不再属于其中：它现在是一个路由。
* 模态框是受控的：父组件持有 `isOpen` 状态并传递 `onClose`.
* 所有面向用户的文本都通过 `useT()` (`lib/i18n/I18nContext`）；CI 强制执行区域设置一致性。
* 生产环境 `app/src` 代码仅使用静态 `import` / `import type`，绝不使用动态导入。

***

## 钩子与工具

### 自定义 Hook（`hooks/`)

`app/src/hooks/` 存放应用级 Hook（ [规模](#scale) 表名指代用于统计它们的命令）。代表性示例：

* `useUser` 是对……的轻量封装 `useCoreState()`；它返回 `{ user: snapshot.currentUser, isLoading, error, refetch }`。没有单独的用户存储。
* `useBackendUrl` 在运行时解析后端 URL（参见 [运行时配置优先级](#runtime-config-precedence)).
* `useThreadQueries` 获取聊天线程。
* `useDaemonHealth` / `useDaemonLifecycle` 跟踪核心服务健康状态。
* `useDeveloperMode`, `useMediaQuery`, `useEscapeKey` 是通用 UI 工具。
* 功能 Hook： `useFlowRunProgress`, `useWorkflowBuilderChat`, `useConsciousItems`, `useIntelligenceStats`, `useCostDashboard`，等等。

功能本地 Hook 与其对应功能一起放在 `features/*/`.

### 工具

#### 配置（`utils/config.ts`)

集中式构建时环境变量访问：切勿直接读取 `import.meta.env` 。不要在其他地方直接读取。这些常量只携带写入打包文件中的值；关于应用在运行时实际通信的 URL，请参见 `services/backendUrl` 以及 `hooks/useBackendUrl`.

```typescript
// 仅构建时回退（在 Tauri 之外使用）。
export const BACKEND_URL = /* VITE_BACKEND_URL || default */;
// 核心 RPC 的构建时回退。
export const CORE_RPC_URL = /* VITE_OPENHUMAN_CORE_RPC_URL || 'http://127.0.0.1:7788/rpc' */;
// 开发标志，例如：
export const DEV_FORCE_ONBOARDING = /* dev-only VITE_DEV_FORCE_ONBOARDING */;
```

> 不要直接导入 `BACKEND_URL` 来发起 API 调用。请在运行时解析 URL，以便核心的 `api_url` （通过 `openhuman.config_resolve_api_url`）生效：
>
> ```typescript
> // React 组件
> import { useBackendUrl } from "../hooks/useBackendUrl";
> const backendUrl = useBackendUrl();
>
> // 非 React 代码
> import { getBackendUrl } from "../services/backendUrl";
> const backendUrl = await getBackendUrl();
> ```

#### 桌面端深度链接监听器（`utils/desktopDeepLinkListener.ts`)

处理传入的 `openhuman://` 通过 Tauri 深度链接插件处理深度链接：解析 URL，在 Rust 端执行令牌交换（绕过 CORS），存储会话，并进行导航。延迟从 `main.tsx` 中设置，以便先让 Tauri IPC 桥准备就绪。

#### URL 打开器（`utils/openUrl.ts`)

跨平台打开 URL：先尝试 Tauri opener 插件，回退到 `window.open`。请始终使用它，而不是直接使用原始的 `window.open` ，以便链接在系统浏览器中打开。

#### Tauri 命令封装（`utils/tauriCommands/`)

围绕……的类型化封装 `invoke(...)`，包括感知桥接差距的 `isTauri()` 守卫（检查 `__TAURI_INTERNALS__.invoke` 是否真正已接线，而不只是应用运行在 Tauri 下）。请使用它；切勿直接检查 `window.__TAURI__` 。

### Polyfills（`polyfills.ts`)

Node.js 全局对象（`Buffer`, `process`, `util`）已为浏览器做了 polyfill。若干浏览器端模块使用了 Node API，包括语音音频编码（`features/human/voice/wavEncoder.ts`）、吉祥物 Rive 资源缓存（`features/human/Mascot/`）以及工具时间线格式化。

有两层提供它们：

1. `vite-plugin-node-polyfills` 在 `app/vite.config.ts` (`buffer`, `process`, `util`, `os`, `crypto`, `stream`，以及 `Buffer`/`process`/`global` 全局对象）。
2. `polyfills.ts`，首先在 `main.tsx`中导入，它会同步赋值 `Buffer`/`process`/`util` 到 `globalThis`/`window`/`global`/`self` ，再由任何依赖模块执行之前完成。

### 最佳实践

#### Hook 依赖项与清理

```typescript
useEffect(() => {
  on("event", handler);
  return () => off("event", handler);
}, [on, off, handler]);
```

始终包含依赖项，并始终清理订阅。

#### 错误处理

将 Tauri/工具调用包裹在 try-catch 中，并提供回退：

```typescript
try {
  await openUrl(url);
} catch (error) {
  console.error("Failed to open URL:", error);
}
```

#### 类型安全

为 API 和 RPC 调用使用 TypeScript 泛型：

```typescript
const user = await apiClient.get<User>("/users/me");
const result = await callCoreRpc<Snapshot>({
  method: "openhuman.app_state_snapshot",
});
```

## 已移除的名称

这些名称仍会出现在旧代码和笔记中。请使用替代项：

| 已移除                                                                                | 替换为                                                        |
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `SettingsModal`, `SettingsModalFrame`, `SettingsModalLayout`, `settingsOverlay.ts` | `/settings/*` ，作为由……渲染的普通路由 `设置`                           |
| `SettingsPanelLayout`, `useSettingsAnimation`, `ProfilePanel`                      | 该 `settingsRouteRegistry` 加上 `components/settings/layout/` |
| `components/settings/search/` 以及 `settingsSearchRegistry`                          | 没有：侧边栏搜索字段已被移除。 `searchKeywords` 保留在注册表条目中。                |
| `WebviewHost` 覆盖层位于 `components/accounts/`                                         | 没有：它随 CEF 提供方 WebView 一起消失了                                |
| 前端 QuickJS 技能引擎                                                                    | Rust 核心中的技能执行                                              |
| `UserProvider`, `AIProvider`, `SkillProvider`                                      | `CoreStateProvider` 用于认证和用户状态；AI 配置和技能在核心中                 |
| `/conversations`, `/accounts` 作为独立页面                                               | `/chat/:threadId?` (`账户`），统一聊天界面                           |

## 另请参见

* [架构](/openhuman/zh/kai-fa/architecture/architecture.md)：该 UI 所呈现的 Rust 端。
* [Tauri 壳](/openhuman/zh/kai-fa/architecture/tauri-shell.md)：提供此 bundle 并拥有 IPC 接口的宿主。
* [Agent 支架](/openhuman/zh/kai-fa/architecture/agent-harness.md)：聊天界面的工具时间线正在渲染的内容。
* [记忆](/openhuman/zh/gong-neng/memory.md)：面向用户的……形态 `/connections?tab=brain` 标签。
* [主题](/openhuman/zh/kai-fa/theming.md)：背后的令牌层 `ThemeProvider` 以及 Theme Studio。
* [iOS 配套端](/openhuman/zh/gong-neng/ios-companion.md)：该实验性移动客户端， `AppRoutesIOS` 外壳以及 `transport/` profiles 所属。
