feat(plugins): add source-aware evidence and recording timelines
This commit is contained in:
1 parent
72ff342605
commit
ef7af9988e
61 files changed
+3847
-108
No files matched your search
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: evidence-demo
|
||||
description: 按任务要求保存文本到任务资料目录,返回文件校验结果。
|
||||
---
|
||||
|
||||
调用 plugin_load 返回映射后的 write_note 工具。只在用户需要保存文本时使用;不是任意文件读写或命令执行接口。
|
||||
|
||||
输入 text 不超过 16 KiB,文件名由工具生成,不能覆盖已有文件。成功后检查回执中的文件名、SHA-256 和执行状态;文件会随单任务全部资料及批次资料导出。
|
||||
|
||||
保存成功不代表用例通过。结果不确定时先核对任务文件,不重复写入。不自动恢复或删除用户文件。
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"schema_version": 2,
|
||||
"id": "evidence-demo",
|
||||
"name": "文件证据示例",
|
||||
"version": "1.0.0",
|
||||
"description": "开发模板:写入任务专属文本文件并返回校验回执。",
|
||||
"artemis_version": ">=2.0.0,<3.0.0",
|
||||
"platforms": ["win32", "linux", "darwin"],
|
||||
"architectures": ["amd64", "arm64"],
|
||||
"server": {"command": "${config.python}", "args": ["${plugin_dir}/server.py"]},
|
||||
"config": {"python": {"label": "含 MCP 依赖的 Python", "default": "python"}},
|
||||
"skill": "SKILL.md",
|
||||
"targets": ["mixed", "computer"],
|
||||
"resources": [],
|
||||
"capabilities": {
|
||||
"kind": "file",
|
||||
"recording": "none",
|
||||
"required_tools": ["write_note"],
|
||||
"tools": {"write_note": {
|
||||
"effect": "write", "observation": false, "idempotent": false,
|
||||
"cancellation": "unknown", "evidence": ["text", "file"]
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Minimal task-owned attachment server. Run through ARTEMIS, not as a shell tool."""
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from mcp.types import CallToolResult, TextContent, ToolAnnotations
|
||||
|
||||
mcp = FastMCP("evidence-demo")
|
||||
|
||||
|
||||
@mcp.tool(
|
||||
annotations=ToolAnnotations(readOnlyHint=False, destructiveHint=False, idempotentHint=False)
|
||||
)
|
||||
def write_note(text: str) -> CallToolResult:
|
||||
"""Save at most 16 KiB of user-requested text as a new task attachment."""
|
||||
encoded = text.encode("utf-8")
|
||||
root = os.environ.get("ARTEMIS_ARTIFACTS_DIR")
|
||||
if not root or len(encoded) > 16384:
|
||||
return CallToolResult(
|
||||
isError=True,
|
||||
content=[TextContent(type="text", text="任务目录缺失或内容超过 16 KiB;未写文件。")],
|
||||
structuredContent={
|
||||
"artemis": {
|
||||
"version": 1,
|
||||
"execution": "not_started",
|
||||
"side_effect": "none",
|
||||
"error_code": "invalid_input",
|
||||
"retryable": False,
|
||||
}
|
||||
},
|
||||
)
|
||||
directory = Path(root)
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
name = f"note-{uuid.uuid4().hex}.txt"
|
||||
path = directory / name
|
||||
with path.open("xb") as handle:
|
||||
handle.write(encoded)
|
||||
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
return CallToolResult(
|
||||
content=[TextContent(type="text", text=f"已保存并读回校验:{name}")],
|
||||
structuredContent={
|
||||
"artemis": {
|
||||
"version": 1,
|
||||
"execution": "completed",
|
||||
"side_effect": "confirmed",
|
||||
"retryable": False,
|
||||
"source": {
|
||||
"kind": "file",
|
||||
"target_id": "task-artifacts",
|
||||
"label": "任务文件",
|
||||
"captured_at": time.time(),
|
||||
},
|
||||
"attachments": [{"name": name, "sha256": digest, "media_type": "text/plain"}],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="stdio")
|
||||
Reference in new issue
Block a user