2026-05-14 15:10:33 +08:00
# 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`
2026-05-16 12:34:48 +08:00
>
2026-05-16 22:03:14 +08:00
> 2026-05-16 口径补充:本文定义的是页面 AI 最小可用工具合同。`mnote.page.save` 只作为页面级兜底写入工具,不再代表长期精确块编辑方案;页面/块级 AI 工具体系路线图已归档到 `design/07-ai/done/7-9-page-block-ai-tooling-roadmap-v1.md`,执行验收继续以 `design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md` 为准。
2026-05-14 15:10:33 +08:00
## 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。