refactor: archive GUI clients; keep minimal TUI with hooks/API
Move Qt/Tauri/full Web SPA under archive/, ship hermit as CLI/TUI plus libcstcore and cst-webui/1 HTTP+SSE (turn lifecycle hooks, capabilities). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+6
-3
@@ -9,9 +9,12 @@
|
||||
| [env_openai.md](env_openai.md) | Provider、模型、API Key、环境变量(`HERMIT_*` / `CST_*`) |
|
||||
| [parliament.md](parliament.md) | 多设备议会 |
|
||||
| [workers.md](workers.md) | 后台任务、worker、job 完成报告 |
|
||||
| [webui.md](webui.md) | 内置 HTTP + SSE WebUI |
|
||||
| [plugins.md](plugins.md) | Skills、插件、MCP |
|
||||
| [webui.md](webui.md) | 引擎 HTTP + SSE API(`cst-webui/1`) |
|
||||
| [webui-schema.md](webui-schema.md) | SSE / state 字段摘要 |
|
||||
| [plugins.md](plugins.md) | Skills、插件、MCP(可替换扩展;非 core 工具) |
|
||||
| [desktop.md](desktop.md) | 归档桌面 GUI 指针 |
|
||||
| [compat.md](compat.md) | 平台兼容 |
|
||||
| [README-windows.md](README-windows.md) | Windows |
|
||||
| [android.md](android.md) | Android / APK |
|
||||
| [android.md](android.md) | Android / APK(归档 Qt 路径) |
|
||||
| [archive/](archive/) | 历史设计笔记(非产品文档) |
|
||||
| [../archive/](../archive/) | 冻结客户端(qt-app / desktop-tauri / web-spa) |
|
||||
|
||||
+13
-12
@@ -1,10 +1,11 @@
|
||||
# CST-agent on Android
|
||||
|
||||
The engine runs on the phone as a plain bionic binary -- no Termux, no proot,
|
||||
no second process for the UI to talk to. It is the same C core the CLI, the
|
||||
TUI and the WebUI use, cross-compiled with the NDK; on the phone the Qt front
|
||||
end (app/) will link `libcstcore.a` in-process and register its own platform
|
||||
tools under the owner `app:android`.
|
||||
no second process for the UI to talk to. It is the same C core the CLI/TUI and
|
||||
`--web` API use, cross-compiled with the NDK. The archived Qt front end
|
||||
(`archive/qt-app/`) can link `libcstcore.a` in-process and register platform
|
||||
tools under the owner `app:android`. Prefer a remote client on `cst-webui/1`
|
||||
for new work.
|
||||
|
||||
Two scripts cover the engine side:
|
||||
|
||||
@@ -163,7 +164,7 @@ have, so the script rewrites the shebang of the build-tools wrappers.
|
||||
provider's environment variable, `CST_API_KEY`), so the phone answers without
|
||||
being asked for the key again. Rotation keeps the session; the sidebar
|
||||
appears in landscape.
|
||||
* Platform tools behind `app:android` (`app/src/platform/android/AndroidTools.cpp`):
|
||||
* Platform tools behind `app:android` (`archive/qt-app/src/platform/android/AndroidTools.cpp`):
|
||||
`notification_send`, `clipboard_get` / `clipboard_set` under the engine's
|
||||
own tool names and `share` (`ACTION_SEND` + chooser) all work on the device.
|
||||
The file
|
||||
@@ -176,20 +177,20 @@ have, so the script rewrites the shebang of the build-tools wrappers.
|
||||
the tool could only say "failed: no exception detail", because Qt clears a
|
||||
Java exception before C++ gets to look at it. The one step that has to fail
|
||||
visibly (a builder, a `PendingIntent`, `build()`, `notify()`) now lives in
|
||||
`app/android/src/xyz/whcself/cstagent/Notifier.java`, which catches the
|
||||
`archive/qt-app/android/src/xyz/whcself/cstagent/Notifier.java`, which catches the
|
||||
throwable and hands the text back for the tool result; the device then
|
||||
reported `notification sent` and the shade had it. androiddeployqt copies
|
||||
`app/android/` with the rest of the package source and Qt's Gradle template
|
||||
`archive/qt-app/android/` with the rest of the package source and Qt's Gradle template
|
||||
puts its `src/` on `java.srcDirs`, so **a class dropped in that directory
|
||||
needs no build-script change** -- the front service of the next step goes
|
||||
there too.
|
||||
* A session's transcript replays a delivered file (`deliver_file`) as a card
|
||||
with Open and Share -- the copy lives next to the session, marked `for_user`
|
||||
so no request ever contains it (see `app/README.md`, "Files the agent
|
||||
so no request ever contains it (see `archive/qt-archive/qt-app/README.md`, "Files the agent
|
||||
delivers").
|
||||
* **A turn survives the app leaving the screen**: `AgentCore::send()` calls
|
||||
`app::holdForeground()`, which starts `CstService`
|
||||
(`app/android/src/xyz/whcself/cstagent/CstService.java`) as a foreground
|
||||
(`archive/qt-app/android/src/xyz/whcself/cstagent/CstService.java`) as a foreground
|
||||
service and takes a partial wake lock; `finishTurn()` stops it again. On the
|
||||
NX789J: with the app sent home and the screen off, the service showed
|
||||
`isForeground=true` with its `CST-agent is working` notification (id 2, the
|
||||
@@ -205,17 +206,17 @@ have, so the script rewrites the shebang of the build-tools wrappers.
|
||||
the foreground the same two hooks wait two minutes (a permission) or five
|
||||
(a question) and then answer "no" with a reason instead of waiting for good.
|
||||
The GUI hop a platform tool makes (`onGuiThread()` in
|
||||
`app/src/platform/android/AndroidTools.cpp`) pings first and gives up after
|
||||
`archive/qt-app/src/platform/android/AndroidTools.cpp`) pings first and gives up after
|
||||
three seconds, and the header shows what the turn is doing
|
||||
(`shell: make -j8`) so a long turn is not mistaken for a stuck one.
|
||||
`--uitest` covers the window side of all this.
|
||||
* **Documents**: `md_to_pdf` (the window's own tool,
|
||||
`app/src/core/PdfTools.cpp`, owner `app:qt`) lays markdown or a workspace
|
||||
`archive/qt-app/src/core/PdfTools.cpp`, owner `app:qt`) lays markdown or a workspace
|
||||
`.md`/`.html` out as an A4 PDF with 15 mm margins and hands it over as a file
|
||||
card, exactly the way `deliver_file` does; `QPrinter`/PrintSupport is not
|
||||
used, so the phone gets it too. Qt's PDF engine embeds a font *subset*, which
|
||||
is why a Chinese page is a few KB rather than the few MB a whole CJK font
|
||||
would cost (see `app/README.md`).
|
||||
would cost (see `archive/qt-archive/qt-app/README.md`).
|
||||
* Still to come: the back gesture, and the icon glyphs -- `ThemeIcon` finds no
|
||||
icon files on the phone and draws empty boxes where the desktop shows Breeze
|
||||
icons.
|
||||
|
||||
+13
-10
@@ -1,20 +1,23 @@
|
||||
# Hermit Desktop (Tauri)
|
||||
# Hermit Desktop(归档)
|
||||
|
||||
Canonical GUI: [`apps/desktop`](../apps/desktop/). Qt under [`app/`](../app/) is frozen ([DEPRECATED](../app/DEPRECATED.md)).
|
||||
桌面 GUI **不在本仓默认交付物中**。参考实现已冻结于:
|
||||
|
||||
## Shape
|
||||
| 树 | 说明 |
|
||||
|----|------|
|
||||
| [`archive/desktop-tauri/`](../archive/desktop-tauri/) | Tauri 2 + React(原 `apps/desktop`) |
|
||||
| [`archive/qt-app/`](../archive/qt-app/) | Qt6/QML(原 `app/`,已 DEPRECATED) |
|
||||
|
||||
- **Shell**: Tauri 2 (Linux / Windows / macOS)
|
||||
- **UI**: React 19 — session rail, chat, tool cards, composer (DeepSeek Harness–like IA)
|
||||
- **Engine**: in-process `libcstcore` via Rust FFI (feature `engine`); without it the host uses a mock / echo path so the UI is developable
|
||||
正式远程客户端请实现 [webui.md](webui.md) / [webui-schema.md](webui-schema.md)
|
||||
(`hermit --web`)。进程内嵌入请用 `libcstcore` + `cst_agent_hooks_t`;归档 FFI
|
||||
见 `archive/desktop-tauri/src-tauri/src/ffi_bridge.*`。
|
||||
|
||||
## Dev
|
||||
## 归档 Tauri 开发(不保证可编)
|
||||
|
||||
```bash
|
||||
nix-shell -p rustc cargo nodejs_22 pkg-config webkitgtk_4_1 gtk3 librsvg openssl curl
|
||||
cd apps/desktop && npm install && npm run tauri dev
|
||||
cd archive/desktop-tauri && npm install && npm run tauri dev
|
||||
```
|
||||
|
||||
## Product env
|
||||
## 产品环境变量
|
||||
|
||||
`HERMIT_*` preferred, `CST_*` accepted (see `hermit_getenv`). Config dir `~/.config/hermit`.
|
||||
`HERMIT_*` 优先,`CST_*` 仍接受(见 `hermit_getenv`)。配置目录 `~/.config/hermit`。
|
||||
|
||||
+7
-2
@@ -1,7 +1,12 @@
|
||||
# 扩展:Skills、插件、MCP 与自定义子 agent
|
||||
|
||||
所有扩展只在默认工具档(`--tools default`)下加载;`--tools minimal` 完全不受影响。
|
||||
下文的“配置目录”指 `~/.config/cst-agent/`(`$XDG_CONFIG_HOME/cst-agent/`;Windows 为 `%APPDATA%\cst-agent\`)。
|
||||
Hermit 的**可替换面**是本页这些扩展;**不是**内置 core 工具
|
||||
(`read_file` / `write_file` / `apply_patch` / `run_shell` / …)或权限/沙箱——那些是
|
||||
agent 契约与 `--tools minimal` 基线,不能当用户插件卸掉。
|
||||
|
||||
所有扩展只在默认工具档(`--tools default`)下加载;`--tools minimal` 时
|
||||
`cst_extensions_load` 直接跳过。下文的“配置目录”优先 `~/.config/hermit/`,
|
||||
若无则回退 `~/.config/cst-agent/`(`$XDG_CONFIG_HOME/…`;Windows 为 `%APPDATA%\…`)。
|
||||
|
||||
| 放在哪 | 什么 | 何时加载 |
|
||||
|---|---|---|
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# cst-webui/1 — event & state schema
|
||||
|
||||
Machine-oriented summary of the HTTP/SSE engine API. Narrative + security:
|
||||
[webui.md](webui.md). Peer/device sockets are separate: [parliament.md](parliament.md).
|
||||
|
||||
Protocol string: `GET /api/state` → `"protocol": "cst-webui/1"`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
`GET /api/state` includes `"capabilities": [ ... ]` listing SSE `type` values this
|
||||
build emits. Clients should probe this array rather than hard-coding.
|
||||
|
||||
Current set:
|
||||
|
||||
| type | Mirrors hook | Notes |
|
||||
|------|--------------|--------|
|
||||
| `assistant_delta` | `on_assistant_delta` | streaming text |
|
||||
| `think_delta` | `on_think_delta` | reasoning stream |
|
||||
| `assistant_html` | (web md) | sanitized HTML chunks |
|
||||
| `status` | `on_status` | `{ "message": "..." }` |
|
||||
| `tool` | `on_tool_event` | phase + call |
|
||||
| `tool_result` | `on_tool_result` | output + exit |
|
||||
| `todo` | `on_todo` | task list text |
|
||||
| `permission` | `on_permission` | wait for `POST /api/permission` |
|
||||
| `ask_user` | `on_ask_user` | wait for `POST /api/ask` |
|
||||
| `state` | — | full snapshot (same shape as `/api/state`) |
|
||||
| `turn_begin` | `on_turn_begin` | `{ "text": "<user prompt>" }` |
|
||||
| `turn_end` | `on_turn_end` | `{ "rc": 0\|N, "error": "" }` |
|
||||
| `error` | `on_error` | `{ "kind": "turn\|cancel\|…", "message": "…" }` |
|
||||
|
||||
## SSE framing
|
||||
|
||||
```
|
||||
GET /api/events?since=<seq>
|
||||
```
|
||||
|
||||
- `id: <seq>` + `data: {"type":…,"seq":…, …}`
|
||||
- Comment heartbeat: `: ping`
|
||||
|
||||
## Primary control endpoints
|
||||
|
||||
| Method | Path | Role |
|
||||
|--------|------|------|
|
||||
| GET | `/api/state` | snapshot + `capabilities` |
|
||||
| GET | `/api/events` | SSE stream |
|
||||
| POST | `/api/turn` | `{ "text": "..." }` start turn |
|
||||
| POST | `/api/cancel` | cancel in-flight turn |
|
||||
| POST | `/api/permission` | answer permission prompt |
|
||||
| POST | `/api/ask` | answer ask_user |
|
||||
| POST | `/api/quit` | shut down server |
|
||||
|
||||
Full route list: [webui.md](webui.md).
|
||||
|
||||
## Embedded hooks (same semantics)
|
||||
|
||||
```c
|
||||
cst_agent_hooks_t {
|
||||
on_status, on_assistant_delta, on_think_delta,
|
||||
on_tool_event, on_tool_result, on_tool_images,
|
||||
on_permission, on_ask_user, on_todo,
|
||||
on_turn_begin, on_turn_end, on_error,
|
||||
userdata
|
||||
};
|
||||
```
|
||||
|
||||
Archived FFI mirror: `archive/desktop-tauri/src-tauri/src/ffi_bridge.h` (`hermit_hooks_t`).
|
||||
+23
-17
@@ -1,32 +1,35 @@
|
||||
# WebUI(内置 HTTP + SSE 前端)
|
||||
# 引擎 HTTP + SSE API(`cst-webui/1`)
|
||||
|
||||
`cst-agent --web` 让同一个进程同时提供命令行以外的前端:浏览器(或任何 HTTP 客户端)
|
||||
连上后能看到流式回答、工具卡片、审批弹窗和任务清单。它是 TUI 之外的第二个前端,
|
||||
**引擎没有为它改一行**——两边用的都是 `cst_agent_hooks_t` 这组回调。
|
||||
`hermit --web`(兼容 `cst-agent --web`)在同一进程上暴露 **远程客户端契约**:
|
||||
任意 HTTP 客户端可订阅 SSE、发起 turn、回答审批。事件与嵌入式
|
||||
`cst_agent_hooks_t` 一一对应——TUI 与 `--web` 共用引擎,不另开私有通道。
|
||||
|
||||
这份文档同时是**引擎 API v1 的规范**:以后专门客户端(Tauri / Qt / 移动端)直接照它实现,
|
||||
不需要读 C 代码。
|
||||
字段级摘要见 [webui-schema.md](webui-schema.md)。设备间 peer 套接字见
|
||||
[parliament.md](parliament.md)(与本 API 无关)。
|
||||
|
||||
默认只嵌入**健康页**;完整浏览器 SPA 参考实现在 `archive/web-spa`。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 默认只监听 127.0.0.1:8787
|
||||
cst-agent --web
|
||||
# 默认只监听 127.0.0.1:8787(健康页 + /api/*)
|
||||
hermit --web
|
||||
|
||||
# 指定端口;0 = 让系统分配(启动日志里会打印实际 URL)
|
||||
cst-agent --web --web-port 8787
|
||||
hermit --web --web-port 8787
|
||||
|
||||
# 让手机/另一台机器访问(必须给 token,否则拒绝启动在非 loopback 上)
|
||||
cst-agent --web --web-host 0.0.0.0 --web-token 我自己定的口令
|
||||
hermit --web --web-host 0.0.0.0 --web-token 我自己定的口令
|
||||
|
||||
# 开发前端:直接从目录读 web/,改完刷新页面即可,不用重新编译
|
||||
cst-agent --web --web-dev ./web
|
||||
# 开发:读磁盘上的静态目录(健康页或归档 SPA)
|
||||
hermit --web --web-dev ./web
|
||||
hermit --web --web-dev archive/web-spa
|
||||
|
||||
# 启动后自动打开浏览器
|
||||
cst-agent --web --web-open
|
||||
hermit --web --web-open
|
||||
|
||||
# 启动即跑一轮(不点页面也能用)
|
||||
cst-agent --web -p "看看这个仓库有什么问题"
|
||||
hermit --web -p "看看这个仓库有什么问题"
|
||||
```
|
||||
|
||||
启动日志:
|
||||
@@ -62,7 +65,8 @@ webui: token 3f9a… (append #token=3f9a… to the URL)
|
||||
## 引擎 API(协议 `cst-webui/1`)
|
||||
|
||||
所有响应都是 JSON(`Content-Type: application/json`)。`GET /api/state` 的
|
||||
`protocol` 字段给出版本号,客户端应当先看它再决定支持哪些端点。
|
||||
`protocol` 字段给出版本号;`capabilities` 数组列出本进程会发出的 SSE `type`。
|
||||
客户端应当先看它们再决定支持哪些端点/事件。
|
||||
|
||||
### 事件流
|
||||
|
||||
@@ -95,9 +99,11 @@ GET /api/events?since=<seq> → text/event-stream (chunked)
|
||||
| `permission_done` | `id`,`answer` | 问题已结束(超时/取消也会发) |
|
||||
| `ask_user` | `id`,`question`,`options[]` | 需要回答 |
|
||||
| `session` | `id` | 切换/新建了会话,客户端应重新拉 `/api/session` |
|
||||
| `turn_end` | `rc`,`error` | 本轮结束(`rc=0` 正常,非 0 见 `error`) |
|
||||
| `turn_begin` | `text` | 一轮开始(用户提示原文);对应 `on_turn_begin` |
|
||||
| `turn_end` | `rc`,`error` | 本轮结束(`rc=0` 正常,非 0 见 `error`);对应 `on_turn_end` |
|
||||
| `error` | `kind`,`message` | 结构化错误(`turn` / `cancel` / …);对应 `on_error`,常在 `turn_end` 之前 |
|
||||
|
||||
`state` 字段:`workspace` `session` `provider` `model` `effort` `approval`
|
||||
`state` 字段:`protocol` `capabilities[]` `workspace` `session` `provider` `model` `effort` `approval`
|
||||
(`ask|auto|yolo`) `plan_mode` `protocol_tools` `status_busy` `todo`
|
||||
`context_tokens` `undo_available` `pending_images[]` `usage{requests,prompt_tokens,
|
||||
completion_tokens,cached_tokens,cache_miss_tokens,cost,priced,currency}`
|
||||
|
||||
Reference in New Issue
Block a user