Files
mnote/rust/crates/mnote-web/src/hermes_tools/manifest.rs
T

621 lines
22 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use serde_json::{json, Value};
pub const MANIFEST_SCHEMA_VERSION: &str = "mnote.hermes_tool_manifest.v1";
pub const TOOL_SCHEMA_VERSION: &str = "mnote.hermes_tool.v1";
pub fn manifest() -> Value {
json!({
"schemaVersion": MANIFEST_SCHEMA_VERSION,
"plugin": {
"name": "mnote",
"description": "mnote 页面、树、artifact 与 edge 工具",
"runtimeOwner": "mnote-web",
"writeOwner": "rust-runtime-kernel"
},
"tools": [
doc_fetch_tool(),
doc_find_tool(),
block_fetch_tool(),
doc_plan_update_tool(),
block_replace_tool(),
block_insert_after_tool(),
block_delete_tool(),
block_move_after_tool(),
doc_apply_block_ops_tool(),
doc_markdown_edit_tool(),
page_get_tool(),
page_save_tool(),
available_tool("mnote.page.update_title", "更新当前页面标题", ["page.write"]),
available_tool("mnote.page.update_options", "更新当前页面设置", ["page.write"]),
mindmap_fetch_tool(),
mindmap_apply_ops_tool(),
office_fetch_summary_tool(),
office_propose_changes_tool(),
available_tool("mnote.artifact.create_summary", "为当前页面创建或更新 AI Summary", ["artifact.write"]),
available_tool("mnote.artifact.create_ai_note", "基于当前页面创建新的 AI Note", ["artifact.write"])
]
})
}
fn base_identity_properties() -> Value {
json!({
"workspaceId": { "type": "string" },
"documentId": { "type": "string" },
"sessionId": { "type": "string" },
"runId": { "type": "string" },
"toolCallId": { "type": "string" },
"traceId": { "type": "string" },
"actorId": { "type": "string" },
"actorType": { "type": "string" }
})
}
fn doc_fetch_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert(
"scope".into(),
json!({ "type": "string", "default": "full" }),
);
map.insert(
"detail".into(),
json!({ "type": "string", "default": "with_ids" }),
);
map.insert(
"format".into(),
json!({ "type": "string", "enum": ["json", "markdown", "text", "page_xml"], "default": "json" }),
);
map.insert("blockId".into(), json!({ "type": "string" }));
map.insert(
"selectedBlockIds".into(),
json!({ "type": "array", "items": { "type": "string" } }),
);
map.insert(
"allowedTargetBlockIds".into(),
json!({ "type": "array", "items": { "type": "string" } }),
);
map.insert("query".into(), json!({ "type": "string" }));
map.insert(
"maxBlocks".into(),
json!({ "type": "integer", "default": 120 }),
);
}
json!({
"name": "mnote.doc.fetch",
"description": "读取当前页面的 canonical block projection,支持 full/outline/block/keyword 范围",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["page.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId"],
"properties": properties
}
})
}
fn doc_find_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("query".into(), json!({ "type": "string" }));
map.insert(
"match".into(),
json!({ "type": "string", "default": "text" }),
);
map.insert("limit".into(), json!({ "type": "integer", "default": 20 }));
}
json!({
"name": "mnote.doc.find",
"description": "在 Page Aggregate block projection 中按文本、类型或 blockId 查找块",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["page.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "query"],
"properties": properties
}
})
}
fn block_fetch_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("blockId".into(), json!({ "type": "string" }));
map.insert(
"includeChildren".into(),
json!({ "type": "boolean", "default": true }),
);
map.insert(
"contextBefore".into(),
json!({ "type": "integer", "default": 1 }),
);
map.insert(
"contextAfter".into(),
json!({ "type": "integer", "default": 1 }),
);
map.insert(
"format".into(),
json!({ "type": "string", "enum": ["json", "markdown", "text", "page_xml"], "default": "json" }),
);
}
json!({
"name": "mnote.block.fetch",
"description": "读取单个块及同父级上下文",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["block.read", "page.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "blockId"],
"properties": properties
}
})
}
fn doc_plan_update_tool() -> Value {
write_tool(
"mnote.doc.plan_update",
"生成页面块更新 dry-run 计划,不直接写入",
["page.write", "block.write"],
json!({
"command": { "type": "string" },
"blockId": { "type": "string" },
"anchorBlockId": { "type": "string" },
"content": { "type": ["string", "object", "array"] },
"query": { "type": "string" },
"revision": { "type": ["number", "string"] },
"conflictDetectionKey": { "type": "string" }
}),
[],
)
}
fn block_replace_tool() -> Value {
write_tool(
"mnote.block.replace",
"兼容块写工具:替换指定块内容;本地 Markdown 普通编辑优先使用 agent 原生 patch/diff,必要时再走 Rust page.body.write 兼容链路",
["block.write", "page.write"],
json!({
"blockId": { "type": "string" },
"content": { "type": ["string", "object", "array"] },
"revision": { "type": ["number", "string"] },
"conflictDetectionKey": { "type": "string" },
"blockRevisionRef": { "type": "string" },
"allowedTargetBlockIds": {
"type": "array",
"items": { "type": "string" }
}
}),
[
"blockId",
"content",
"revision",
"conflictDetectionKey",
"blockRevisionRef",
],
)
}
fn block_insert_after_tool() -> Value {
let mut tool = write_tool(
"mnote.block.insert_after",
"在指定块后插入新块;真实写入走 Rust page.body.save 链路",
["block.write", "page.write"],
json!({
"anchorBlockId": { "type": "string" },
"content": { "type": ["string", "object", "array"] },
"block": { "type": "object" },
"blocks": {
"type": "array",
"minItems": 1,
"maxItems": 20,
"items": { "type": ["string", "object", "array"] }
},
"revision": { "type": ["number", "string"] },
"conflictDetectionKey": { "type": "string" },
"anchorRevisionRef": { "type": "string" },
"allowedTargetBlockIds": {
"type": "array",
"items": { "type": "string" }
}
}),
[
"anchorBlockId",
"revision",
"conflictDetectionKey",
"anchorRevisionRef",
],
);
if let Some(schema) = tool.get_mut("inputSchema").and_then(Value::as_object_mut) {
schema.insert(
"anyOf".into(),
json!([
{ "required": ["content"] },
{ "required": ["block"] },
{ "required": ["blocks"] }
]),
);
}
tool
}
fn block_move_after_tool() -> Value {
write_tool(
"mnote.block.move_after",
"受限同父级普通叶子块移动;真实写入走 Rust page.body.save 链路",
["block.write", "page.write"],
json!({
"blockId": { "type": "string" },
"anchorBlockId": { "type": "string" },
"revision": { "type": ["number", "string"] },
"conflictDetectionKey": { "type": "string" },
"blockRevisionRef": { "type": "string" },
"anchorRevisionRef": { "type": "string" },
"allowedTargetBlockIds": {
"type": "array",
"items": { "type": "string" }
}
}),
[
"blockId",
"anchorBlockId",
"revision",
"conflictDetectionKey",
"blockRevisionRef",
"anchorRevisionRef",
],
)
}
fn block_delete_tool() -> Value {
write_tool(
"mnote.block.delete",
"删除指定无子块普通块;真实写入走 Rust page.body.save 链路",
["block.write", "page.write"],
json!({
"blockId": { "type": "string" },
"revision": { "type": ["number", "string"] },
"conflictDetectionKey": { "type": "string" },
"blockRevisionRef": { "type": "string" },
"allowedTargetBlockIds": {
"type": "array",
"items": { "type": "string" }
}
}),
[
"blockId",
"revision",
"conflictDetectionKey",
"blockRevisionRef",
],
)
}
fn doc_apply_block_ops_tool() -> Value {
write_tool(
"mnote.doc.apply_block_ops",
"一次性应用多个块级操作;Rust 侧统一读取最新 projection、生成 canonical content 并一次保存,适合页面 AI 小段落增删改移动快路径",
["block.write", "page.write"],
json!({
"operations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"op": { "type": "string" },
"blockId": { "type": "string" },
"anchorBlockId": { "type": "string" },
"matchText": { "type": "string" },
"anchorText": { "type": "string" },
"afterText": { "type": "string" },
"allowedTargetBlockIds": {
"type": "array",
"items": { "type": "string" }
},
"content": { "type": ["string", "object", "array"] }
}
}
}
}),
["operations"],
)
}
fn write_tool(
name: &str,
description: &str,
scope: impl IntoIterator<Item = &'static str>,
extra_properties: Value,
extra_required: impl IntoIterator<Item = &'static str>,
) -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("dryRun".into(), json!({ "type": "boolean" }));
map.insert("idempotencyKey".into(), json!({ "type": "string" }));
if let Value::Object(extra) = extra_properties {
for (key, value) in extra {
map.insert(key, value);
}
}
}
let mut required = vec![
"workspaceId",
"documentId",
"sessionId",
"runId",
"toolCallId",
"traceId",
"actorId",
"dryRun",
"idempotencyKey",
];
required.extend(extra_required);
json!({
"name": name,
"description": description,
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": scope.into_iter().collect::<Vec<_>>(),
"status": "available",
"annotations": tool_annotations(false, false, false, false),
"inputSchema": {
"type": "object",
"required": required,
"properties": properties
}
})
}
fn page_get_tool() -> Value {
json!({
"name": "mnote.page.get",
"description": "读取当前页面 Page Aggregate 摘要",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["page.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId"],
"properties": {
"workspaceId": { "type": "string" },
"documentId": { "type": "string" },
"sessionId": { "type": "string" },
"runId": { "type": "string" },
"toolCallId": { "type": "string" },
"traceId": { "type": "string" },
"actorId": { "type": "string" },
"actorType": { "type": "string" },
"includeBody": { "type": "boolean", "default": true },
"includeOptions": { "type": "boolean", "default": true },
"includeBlocks": { "type": "boolean", "default": true }
}
}
})
}
fn page_save_tool() -> Value {
json!({
"name": "mnote.page.save",
"description": "粗粒度兼容兜底:保存当前页面正文;本地 Markdown 普通编辑优先使用 agent 原生 patch/diff,只有整页覆盖/追加且其它工具无法表达时使用",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["page.write"],
"status": "available",
"annotations": tool_annotations(false, true, false, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "content", "dryRun", "idempotencyKey"],
"properties": {
"workspaceId": { "type": "string" },
"documentId": { "type": "string" },
"sessionId": { "type": "string" },
"runId": { "type": "string" },
"toolCallId": { "type": "string" },
"traceId": { "type": "string" },
"actorId": { "type": "string" },
"actorType": { "type": "string" },
"content": {
"description": "要写入的正文块数组、{blocks:[...]}、TipTap content 数组或纯文本",
"type": ["array", "object", "string"]
},
"mode": {
"type": "string",
"enum": ["replace", "append", "prepend"],
"default": "replace"
},
"dryRun": { "type": "boolean" },
"idempotencyKey": { "type": "string" }
}
}
})
}
fn available_tool(
name: &str,
description: &str,
scope: impl IntoIterator<Item = &'static str>,
) -> Value {
json!({
"name": name,
"description": description,
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": scope.into_iter().collect::<Vec<_>>(),
"status": "available",
"annotations": tool_annotations(false, false, false, false)
})
}
fn doc_markdown_edit_tool() -> Value {
let mut tool = write_tool(
"mnote.doc.markdown_edit",
"兼容 / 远端代理 fallback:通过文本级搜索替换编辑 markdown 内容。local-first 本地 workspace 默认优先让 agent 原生 patch/diff 直接编辑授权文件;仅在需要 MNote 兼容工具、远端代理或结构校验时使用。",
["block.write", "page.write"],
json!({
"operations": {
"type": "array",
"description": "搜索替换操作列表",
"items": {
"type": "object",
"properties": {
"search": { "type": "string", "description": "要搜索的原文片段" },
"replace": { "type": "string", "description": "替换后的新文本" }
},
"required": ["search", "replace"]
}
},
"full_content": {
"type": "string",
"description": "完整修改后的 markdown 文本(替代 operations"
}
}),
[],
);
if let Some(schema) = tool.get_mut("inputSchema").and_then(Value::as_object_mut) {
schema.insert(
"anyOf".into(),
json!([
{ "required": ["operations"] },
{ "required": ["full_content"] }
]),
);
}
tool
}
fn resource_identity_properties(resource_id_name: &str) -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("rootUri".into(), json!({ "type": "string" }));
map.insert("sourceKind".into(), json!({ "type": "string" }));
map.insert(resource_id_name.into(), json!({ "type": "string" }));
map.insert("resourcePath".into(), json!({ "type": "string" }));
map.insert(
"aiAccessScope".into(),
json!({
"type": "object",
"properties": {
"permissionLevel": { "type": "string" },
"allowedResourceIds": { "type": "array", "items": { "type": "string" } },
"allowedRoots": { "type": "array" }
}
}),
);
}
properties
}
fn mindmap_fetch_tool() -> Value {
let mut properties = resource_identity_properties("mindmapId");
if let Value::Object(map) = &mut properties {
map.insert(
"scope".into(),
json!({ "type": "string", "enum": ["tree", "subtree", "markdown_summary"], "default": "tree" }),
);
map.insert("nodeId".into(), json!({ "type": "string" }));
}
json!({
"name": "mnote.mindmap.fetch",
"description": "读取授权 root 内的 mindmap resource 结构或 markdown summary",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["resource.read", "mindmap.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "mindmapId", "sessionId", "runId", "toolCallId", "traceId", "actorId"],
"properties": properties
}
})
}
fn mindmap_apply_ops_tool() -> Value {
write_tool(
"mnote.mindmap.apply_ops",
"对授权 mindmap resource 生成或执行结构操作;本地 workspace 默认建议 agent 直接编辑授权文件,必要时才走该工具",
["resource.write", "mindmap.write"],
json!({
"mindmapId": { "type": "string" },
"rootUri": { "type": "string" },
"sourceKind": { "type": "string" },
"resourcePath": { "type": "string" },
"expectedRevision": { "type": ["number", "string"] },
"ops": {
"type": "array",
"items": { "type": "object" }
},
"aiAccessScope": { "type": "object" }
}),
["mindmapId", "ops"],
)
}
fn office_fetch_summary_tool() -> Value {
let mut properties = resource_identity_properties("assetId");
if let Value::Object(map) = &mut properties {
map.insert(
"extractMode".into(),
json!({ "type": "string", "enum": ["text", "outline", "metadata"], "default": "metadata" }),
);
}
json!({
"name": "mnote.office.fetch_summary",
"description": "读取授权 Office resource 的元数据和轻量文本摘要;不直接写二进制文件",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["resource.read", "office.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "assetId", "sessionId", "runId", "toolCallId", "traceId", "actorId"],
"properties": properties
}
})
}
fn office_propose_changes_tool() -> Value {
let mut properties = resource_identity_properties("assetId");
if let Value::Object(map) = &mut properties {
map.insert("instructions".into(), json!({ "type": "string" }));
map.insert(
"basisRevision".into(),
json!({ "type": ["number", "string"] }),
);
}
json!({
"name": "mnote.office.propose_changes",
"description": "基于授权 Office resource 生成修改建议;真实写入仍由 OnlyOffice / officecli 完成",
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": ["resource.read", "office.read"],
"status": "available",
"annotations": tool_annotations(true, false, true, false),
"inputSchema": {
"type": "object",
"required": ["workspaceId", "documentId", "assetId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "instructions"],
"properties": properties
}
})
}
fn tool_annotations(
readonly: bool,
destructive: bool,
idempotent: bool,
requires_approval: bool,
) -> Value {
json!({
"readonly": readonly,
"destructive": destructive,
"idempotent": idempotent,
"readOnly": readonly,
"requiresWritePermission": !readonly,
"requiresApproval": requires_approval,
"approvalMode": if requires_approval { "review" } else { "yolo" },
"runtimeOwner": "mnote-web",
"writeOwner": "rust-runtime-kernel",
"selectionEffect": if readonly { "preserve" } else { "may_change" }
})
}