# 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 口径补充：本文定义的是页面 AI 最小可用工具合同。`mnote.page.save` 只作为页面级兜底写入工具，不再代表长期精确块编辑方案；页面/块级 AI 工具体系路线图已归档到 `design/07-ai/done/7-9-page-block-ai-tooling-roadmap-v1.md`。
>
> 2026-05-22 归档治理补充：旧 `7-10` 已移入 `design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`，不再作为当前执行验收入口；`7-14` 也已移入 `design/old/07-ai/process/7-14-local-first-ai-markdown-editing-convergence-v1.md`。当前 AI 编辑 active 口径以 `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 为准，MNote 不再提供普通 Markdown 编辑工具。

## 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。
