305 lines
7.7 KiB
Markdown
305 lines
7.7 KiB
Markdown
# 7-6 [done] mnote Hermes plugin tool 合同 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/plugins.ts`、`packages/client/src/api/hermes/skills.ts`、`packages/server/src/services/hermes/plugins.ts`、`packages/server/src/services/hermes/chat-run-socket.ts`
|
|||
|
|
|
|||
|
|
## 1. 总边界
|
|||
|
|
|
|||
|
|
- [x] Hermes 看到的工具名必须是 `mnote.*`,不是 `mnote-cli`、Next route 或前端私有函数。
|
|||
|
|
- [x] plugin 内部可以临时调用 `mnote-cli` JSON adapter,但这只是内部 adapter,不是长期 tool 名称。
|
|||
|
|
- [x] Hermes plugin 不直接写 Convex;所有写入必须回到 Rust runtime / kernel。
|
|||
|
|
- [x] mnote 只保存业务事实、audit、artifact、edge、page/body/title/options 结果,不保存 Hermes 聊天历史。
|
|||
|
|
- [x] 所有 tool call 必须携带 `sessionId/runId/toolCallId/traceId` 便于串联 Hermes run 与 Rust command。
|
|||
|
|
|
|||
|
|
## 2. 统一入参
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"toolName": "mnote.page.get",
|
|||
|
|
"workspaceId": "ws_1",
|
|||
|
|
"documentId": "doc_1",
|
|||
|
|
"actorId": "user_1",
|
|||
|
|
"sessionId": "mnote_doc_1_trace_1",
|
|||
|
|
"runId": "run_123",
|
|||
|
|
"toolCallId": "call_123",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"idempotencyKey": "idem_123",
|
|||
|
|
"dryRun": false,
|
|||
|
|
"capabilityScope": ["page.read"],
|
|||
|
|
"args": {}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. 统一出参
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": true,
|
|||
|
|
"toolName": "mnote.page.get",
|
|||
|
|
"toolCallId": "call_123",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"result": {},
|
|||
|
|
"audit": {
|
|||
|
|
"effect": "read",
|
|||
|
|
"commandId": null,
|
|||
|
|
"workspaceId": "ws_1",
|
|||
|
|
"documentId": "doc_1"
|
|||
|
|
},
|
|||
|
|
"error": null
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
失败:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": false,
|
|||
|
|
"toolName": "mnote.page.save",
|
|||
|
|
"toolCallId": "call_123",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"result": null,
|
|||
|
|
"audit": {
|
|||
|
|
"effect": "none",
|
|||
|
|
"workspaceId": "ws_1",
|
|||
|
|
"documentId": "doc_1"
|
|||
|
|
},
|
|||
|
|
"error": {
|
|||
|
|
"code": "mnote_tool_permission_denied",
|
|||
|
|
"message": "当前用户没有页面写权限"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. 第一批工具 schema
|
|||
|
|
|
|||
|
|
### `mnote.page.get`
|
|||
|
|
|
|||
|
|
入参:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"type": "object",
|
|||
|
|
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId"],
|
|||
|
|
"properties": {
|
|||
|
|
"workspaceId": { "type": "string" },
|
|||
|
|
"documentId": { "type": "string" },
|
|||
|
|
"includeBody": { "type": "boolean", "default": true },
|
|||
|
|
"includeOptions": { "type": "boolean", "default": true },
|
|||
|
|
"includeBlocks": { "type": "boolean", "default": true }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
成功:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": true,
|
|||
|
|
"toolName": "mnote.page.get",
|
|||
|
|
"toolCallId": "call_get_1",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"result": {
|
|||
|
|
"documentId": "doc_1",
|
|||
|
|
"workspaceId": "ws_1",
|
|||
|
|
"title": "项目计划",
|
|||
|
|
"bodySummary": "第一段...",
|
|||
|
|
"pageOptions": { "wideLayout": true },
|
|||
|
|
"blocks": [{ "id": "heading_1", "type": "heading", "text": "章节一" }]
|
|||
|
|
},
|
|||
|
|
"audit": { "effect": "read", "commandId": null }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
失败:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": false,
|
|||
|
|
"toolName": "mnote.page.get",
|
|||
|
|
"toolCallId": "call_get_1",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"error": { "code": "mnote_tool_permission_denied", "message": "无页面读取权限" }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `mnote.page.save`
|
|||
|
|
|
|||
|
|
入参:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"type": "object",
|
|||
|
|
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun", "content"],
|
|||
|
|
"properties": {
|
|||
|
|
"workspaceId": { "type": "string" },
|
|||
|
|
"documentId": { "type": "string" },
|
|||
|
|
"content": { "type": "array" },
|
|||
|
|
"mode": { "type": "string", "enum": ["replace", "append"] },
|
|||
|
|
"idempotencyKey": { "type": "string" },
|
|||
|
|
"dryRun": { "type": "boolean" }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
成功:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": true,
|
|||
|
|
"toolName": "mnote.page.save",
|
|||
|
|
"toolCallId": "call_save_1",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"result": { "commandName": "page.body.save", "revision": 8 },
|
|||
|
|
"audit": { "effect": "write", "commandId": "page_body_save_trace_1" }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
dryRun:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": true,
|
|||
|
|
"toolName": "mnote.page.save",
|
|||
|
|
"toolCallId": "call_save_1",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"result": { "dryRun": true, "diff": [{ "op": "append", "blocks": 1 }] },
|
|||
|
|
"audit": { "effect": "dry_run", "commandId": null }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
权限失败:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": false,
|
|||
|
|
"toolName": "mnote.page.save",
|
|||
|
|
"toolCallId": "call_save_1",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"error": { "code": "mnote_tool_permission_denied", "message": "无页面写权限" }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
业务失败:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": false,
|
|||
|
|
"toolName": "mnote.page.save",
|
|||
|
|
"toolCallId": "call_save_1",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"error": { "code": "mnote_tool_conflict", "message": "页面版本冲突,需要刷新后重试" }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `mnote.page.update_title`
|
|||
|
|
|
|||
|
|
入参:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"type": "object",
|
|||
|
|
"required": ["workspaceId", "documentId", "title", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
|||
|
|
"properties": {
|
|||
|
|
"workspaceId": { "type": "string" },
|
|||
|
|
"documentId": { "type": "string" },
|
|||
|
|
"title": { "type": "string", "minLength": 1 },
|
|||
|
|
"idempotencyKey": { "type": "string" },
|
|||
|
|
"dryRun": { "type": "boolean" }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
成功、dryRun、权限失败、业务失败响应形状同 `mnote.page.save`,成功 command 为 `page.head.updateTitle`。
|
|||
|
|
|
|||
|
|
### `mnote.page.update_options`
|
|||
|
|
|
|||
|
|
入参:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"type": "object",
|
|||
|
|
"required": ["workspaceId", "documentId", "options", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
|||
|
|
"properties": {
|
|||
|
|
"workspaceId": { "type": "string" },
|
|||
|
|
"documentId": { "type": "string" },
|
|||
|
|
"options": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"wideLayout": { "type": "boolean" },
|
|||
|
|
"smallText": { "type": "boolean" },
|
|||
|
|
"showToc": { "type": "boolean" },
|
|||
|
|
"protectEditing": { "type": "boolean" }
|
|||
|
|
},
|
|||
|
|
"additionalProperties": false
|
|||
|
|
},
|
|||
|
|
"idempotencyKey": { "type": "string" },
|
|||
|
|
"dryRun": { "type": "boolean" }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
成功、dryRun、权限失败、业务失败响应形状同 `mnote.page.save`,成功 command 为 `page.layout.updateOptions`。`runtimeSupport !== "wired"` 的字段必须返回 dryRun 警告或业务失败。
|
|||
|
|
|
|||
|
|
### `mnote.artifact.create_summary`
|
|||
|
|
|
|||
|
|
入参:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"type": "object",
|
|||
|
|
"required": ["workspaceId", "documentId", "summary", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
|||
|
|
"properties": {
|
|||
|
|
"workspaceId": { "type": "string" },
|
|||
|
|
"documentId": { "type": "string" },
|
|||
|
|
"summary": { "type": "string" },
|
|||
|
|
"idempotencyKey": { "type": "string" },
|
|||
|
|
"dryRun": { "type": "boolean" }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
成功:创建或更新当前页面唯一 summary node,并创建或确认 reference edge。
|
|||
|
|
|
|||
|
|
失败示例:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"ok": false,
|
|||
|
|
"toolName": "mnote.artifact.create_summary",
|
|||
|
|
"toolCallId": "call_summary_1",
|
|||
|
|
"traceId": "trace_1",
|
|||
|
|
"error": { "code": "mnote_tool_idempotency_conflict", "message": "同一幂等键已用于不同 summary 内容" }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `mnote.artifact.create_ai_note`
|
|||
|
|
|
|||
|
|
入参:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"type": "object",
|
|||
|
|
"required": ["workspaceId", "documentId", "content", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
|||
|
|
"properties": {
|
|||
|
|
"workspaceId": { "type": "string" },
|
|||
|
|
"documentId": { "type": "string" },
|
|||
|
|
"content": { "type": "string" },
|
|||
|
|
"idempotencyKey": { "type": "string" },
|
|||
|
|
"dryRun": { "type": "boolean" }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
成功:每次创建独立 ai_note node,并创建 reference edge。
|
|||
|
|
|
|||
|
|
## 5. 权限、幂等与 dryRun
|
|||
|
|
|
|||
|
|
- [x] 权限校验点:登录、workspace member、页面读、页面写、artifact 写。
|
|||
|
|
- [x] 同一 `idempotencyKey` 重试不得重复创建 artifact 或重复写正文。
|
|||
|
|
- [x] `dryRun=true` 只能返回计划和 diff,不得写入。
|
|||
|
|
- [x] 所有失败响应不得泄露无权限页面标题、正文或 artifact 内容。
|
|||
|
|
- [x] 写工具返回的 `audit.commandId` 必须能追踪到 Rust runtime / kernel command。
|