Files
mnote/design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md
T

244 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 7-5 [done] Hermes client proxy 合同 v1
> 更新时间:2026-05-14
>
> 上位依据:`design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`、`design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
>
> Hermes Web UI 参考:`packages/client/src/api/hermes/chat.ts`、`packages/client/src/api/hermes/sessions.ts`、`packages/server/src/routes/hermes/proxy-handler.ts`、`packages/server/src/services/hermes/chat-run-socket.ts`
## 1. 边界
- [x] 浏览器只访问 mnote-web 同源 `/api/hermes/client/*`
- [x] Hermes API key / gateway token 只存在 mnote-web 服务端环境变量。
- [x] mnote-web proxy 只做 auth、同源安全、页面上下文注入、trace 注入和错误码标准化。
- [x] mnote-web proxy 不保存 Hermes session、message、tool event、usage、model 真相。
- [x] `pageContext` 是 run 输入上下文,不是 Hermes session 的长期事实源。
- [x]`/api/ai-agent/run` 在并存期只作为 legacy endpoint,不是新 Hermes 面板主代理。
## 2. 路由
### `GET /api/hermes/client/sessions`
请求:
```http
GET /api/hermes/client/sessions?workspaceId=ws_1&documentId=doc_1&limit=20
```
响应:
```json
{
"ok": true,
"traceId": "trace_1",
"sessions": [
{
"sessionId": "mnote_doc_1_20260514",
"title": "当前页问答",
"preview": "请总结当前页面",
"messageCount": 2,
"toolCallCount": 1,
"updatedAt": 1778712000,
"model": "hermes-agent"
}
]
}
```
### `POST /api/hermes/client/sessions`
请求:
```json
{
"workspaceId": "ws_1",
"documentId": "doc_1",
"traceId": "trace_1",
"title": "当前页问答"
}
```
响应:
```json
{
"ok": true,
"sessionId": "mnote_doc_1_trace_1",
"traceId": "trace_1",
"persistence": "hermes_on_first_run"
}
```
说明:Hermes Web UI 参考实现没有独立 session create HTTP route,客户端生成 session id,首次 run 时由 Hermes 持久化。mnote 保留本 route 是为了同源客户端合同稳定,但不得在 mnote 保存聊天历史。
### `GET /api/hermes/client/sessions/{session_id}`
请求:
```http
GET /api/hermes/client/sessions/mnote_doc_1_trace_1?workspaceId=ws_1&documentId=doc_1
```
响应:
```json
{
"ok": true,
"sessionId": "mnote_doc_1_trace_1",
"traceId": "trace_1",
"messages": [
{
"messageId": "42",
"role": "assistant",
"content": "当前页标题是...",
"toolCallId": null,
"toolName": null,
"timestamp": 1778712000,
"reasoning": null
}
],
"usage": {
"inputTokens": 120,
"outputTokens": 30,
"totalTokens": 150
}
}
```
### `POST /api/hermes/client/runs`
请求:
```json
{
"workspaceId": "ws_1",
"documentId": "doc_1",
"sessionId": "mnote_doc_1_trace_1",
"message": "概括当前页面标题和第一段",
"model": "hermes-agent",
"pageContext": {
"title": "项目计划",
"outline": [],
"pageOptions": { "wideLayout": true }
},
"selectedBlockId": null,
"selectedText": null,
"traceId": "trace_1"
}
```
响应:
```json
{
"ok": true,
"sessionId": "mnote_doc_1_trace_1",
"runId": "run_123",
"messageId": null,
"events": [],
"traceId": "trace_1"
}
```
### `GET /api/hermes/client/events/{run_id}`
请求:
```http
GET /api/hermes/client/events/run_123?sessionId=mnote_doc_1_trace_1
```
响应:`text/event-stream`
```text
data: {"event":"message.delta","run_id":"run_123","session_id":"mnote_doc_1_trace_1","delta":"当前页"}
data: {"event":"run.completed","run_id":"run_123","session_id":"mnote_doc_1_trace_1","output":"当前页...","usage":{"input_tokens":120,"output_tokens":30,"total_tokens":150}}
```
### `POST /api/hermes/client/runs/{run_id}/abort`
请求:
```json
{
"workspaceId": "ws_1",
"documentId": "doc_1",
"sessionId": "mnote_doc_1_trace_1",
"traceId": "trace_1"
}
```
响应:
```json
{
"ok": true,
"runId": "run_123",
"sessionId": "mnote_doc_1_trace_1",
"traceId": "trace_1",
"events": [
{ "event": "abort.started", "run_id": "run_123" }
]
}
```
### `GET /api/hermes/client/models`
响应:
```json
{
"ok": true,
"traceId": "trace_1",
"defaultModel": "hermes-agent",
"models": [
{ "id": "hermes-agent", "label": "Hermes Agent", "provider": "hermes" }
]
}
```
### `GET /api/hermes/client/tools`
响应:
```json
{
"ok": true,
"traceId": "trace_1",
"tools": [
{ "name": "mnote.page.get", "scope": "page.read", "schemaVersion": "mnote.hermes_tool.v1" }
]
}
```
## 3. 错误响应
统一错误体:
```json
{
"ok": false,
"code": "hermes_client_unconfigured",
"message": "Hermes client proxy 未配置 upstream",
"traceId": "trace_1",
"requestId": "req_1"
}
```
稳定错误码:
- [x] `hermes_client_unauthorized`:未登录或缺少有效 mnote 会话。
- [x] `hermes_client_unconfigured`:未配置 `MNOTE_WEB_HERMES_UPSTREAM_URL`
- [x] `hermes_client_bad_request`:请求 JSON 或必要字段错误。
- [x] `hermes_client_upstream_unauthorized`Hermes upstream 拒绝服务端 token。
- [x] `hermes_client_upstream_rate_limited`Hermes upstream 429。
- [x] `hermes_client_upstream_unavailable`Hermes upstream 连接失败或 5xx。
## 4. 并存期规则
- [x] 新页面 AI 面板只允许请求 `/api/hermes/client/*`
- [x] `/api/ai-agent/run` 保留为 legacy guard,不再承载 Hermes 页面 AI 主链。
- [x] `provider=hermes` 不再通过旧 `/api/ai-agent/run` 表达。
- [x] 所有新 smoke 应断言页面 AI 主链没有 `/api/ai-agent/run` 请求。