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

5.5 KiB
Raw Blame History

7-5 [done] Hermes client proxy 合同 v1

更新时间:2026-05-14

上位依据:design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.mddesign/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md

Hermes Web UI 参考:packages/client/src/api/hermes/chat.tspackages/client/src/api/hermes/sessions.tspackages/server/src/routes/hermes/proxy-handler.tspackages/server/src/services/hermes/chat-run-socket.ts

1. 边界

  • 浏览器只访问 mnote-web 同源 /api/hermes/client/*
  • Hermes API key / gateway token 只存在 mnote-web 服务端环境变量。
  • mnote-web proxy 只做 auth、同源安全、页面上下文注入、trace 注入和错误码标准化。
  • mnote-web proxy 不保存 Hermes session、message、tool event、usage、model 真相。
  • pageContext 是 run 输入上下文,不是 Hermes session 的长期事实源。
  • /api/ai-agent/run 在并存期只作为 legacy endpoint,不是新 Hermes 面板主代理。

2. 路由

GET /api/hermes/client/sessions

请求:

GET /api/hermes/client/sessions?workspaceId=ws_1&documentId=doc_1&limit=20

响应:

{
  "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

请求:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "traceId": "trace_1",
  "title": "当前页问答"
}

响应:

{
  "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}

请求:

GET /api/hermes/client/sessions/mnote_doc_1_trace_1?workspaceId=ws_1&documentId=doc_1

响应:

{
  "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

请求:

{
  "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"
}

响应:

{
  "ok": true,
  "sessionId": "mnote_doc_1_trace_1",
  "runId": "run_123",
  "messageId": null,
  "events": [],
  "traceId": "trace_1"
}

GET /api/hermes/client/events/{run_id}

请求:

GET /api/hermes/client/events/run_123?sessionId=mnote_doc_1_trace_1

响应:text/event-stream

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

请求:

{
  "workspaceId": "ws_1",
  "documentId": "doc_1",
  "sessionId": "mnote_doc_1_trace_1",
  "traceId": "trace_1"
}

响应:

{
  "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

响应:

{
  "ok": true,
  "traceId": "trace_1",
  "defaultModel": "hermes-agent",
  "models": [
    { "id": "hermes-agent", "label": "Hermes Agent", "provider": "hermes" }
  ]
}

GET /api/hermes/client/tools

响应:

{
  "ok": true,
  "traceId": "trace_1",
  "tools": [
    { "name": "mnote.page.get", "scope": "page.read", "schemaVersion": "mnote.hermes_tool.v1" }
  ]
}

3. 错误响应

统一错误体:

{
  "ok": false,
  "code": "hermes_client_unconfigured",
  "message": "Hermes client proxy 未配置 upstream",
  "traceId": "trace_1",
  "requestId": "req_1"
}

稳定错误码:

  • hermes_client_unauthorized:未登录或缺少有效 mnote 会话。
  • hermes_client_unconfigured:未配置 MNOTE_WEB_HERMES_UPSTREAM_URL
  • hermes_client_bad_request:请求 JSON 或必要字段错误。
  • hermes_client_upstream_unauthorizedHermes upstream 拒绝服务端 token。
  • hermes_client_upstream_rate_limitedHermes upstream 429。
  • hermes_client_upstream_unavailableHermes upstream 连接失败或 5xx。

4. 并存期规则

  • 新页面 AI 面板只允许请求 /api/hermes/client/*
  • /api/ai-agent/run 保留为 legacy guard,不再承载 Hermes 页面 AI 主链。
  • provider=hermes 不再通过旧 /api/ai-agent/run 表达。
  • 所有新 smoke 应断言页面 AI 主链没有 /api/ai-agent/run 请求。