Files
mnote/design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md
T

305 lines
7.7 KiB
Markdown
Raw Normal View History

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