release(v0.2.0): streaming, MCP protocol, Browser Hand, security enhancements

## Major Features

### Streaming Response System
- Implement LlmDriver trait with `stream()` method returning async Stream
- Add SSE parsing for Anthropic and OpenAI API streaming
- Integrate Tauri event system for frontend streaming (`stream:chunk` events)
- Add StreamChunk types: Delta, ToolStart, ToolEnd, Complete, Error

### MCP Protocol Implementation
- Add MCP JSON-RPC 2.0 types (mcp_types.rs)
- Implement stdio-based MCP transport (mcp_transport.rs)
- Support tool discovery, execution, and resource operations

### Browser Hand Implementation
- Complete browser automation with Playwright-style actions
- Support Navigate, Click, Type, Scrape, Screenshot, Wait actions
- Add educational Hands: Whiteboard, Slideshow, Speech, Quiz

### Security Enhancements
- Implement command whitelist/blacklist for shell_exec tool
- Add SSRF protection with private IP blocking
- Create security.toml configuration file

## Test Improvements
- Fix test import paths (security-utils, setup)
- Fix vi.mock hoisting issues with vi.hoisted()
- Update test expectations for validateUrl and sanitizeFilename
- Add getUnsupportedLocalGatewayStatus mock

## Documentation Updates
- Update architecture documentation
- Improve configuration reference
- Add quick-start guide updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-24 03:24:24 +08:00
parent e49ba4460b
commit 3ff08faa56
78 changed files with 29575 additions and 1682 deletions

121
hands/quiz.HAND.toml Normal file
View File

@@ -0,0 +1,121 @@
# Quiz Hand - 测验生成与评估能力包
#
# ZCLAW Hand 配置
# 提供测验题目生成、答题评估和反馈能力
[hand]
name = "quiz"
version = "1.0.0"
description = "测验能力包 - 生成测验题目、评估答案、提供反馈"
author = "ZCLAW Team"
type = "education"
requires_approval = false
timeout = 60
max_concurrent = 5
tags = ["quiz", "test", "assessment", "education", "learning", "evaluation"]
[hand.config]
# 支持的题型
supported_question_types = [
"multiple_choice",
"true_false",
"fill_blank",
"short_answer",
"matching",
"ordering",
"essay"
]
# 默认难度: easy, medium, hard, adaptive
default_difficulty = "medium"
# 每次生成的题目数量
default_question_count = 5
# 是否提供解析
show_explanation = true
# 是否显示正确答案
show_correct_answer = true
# 答案反馈模式: immediate, after_submit, after_all
feedback_mode = "immediate"
# 评分方式: exact, partial, rubric
grading_mode = "exact"
# 及格分数(百分比)
passing_score = 60
[hand.triggers]
manual = true
schedule = false
webhook = false
[[hand.triggers.events]]
type = "chat.intent"
pattern = "测验|测试|题目|考核|quiz|test|question|exam"
priority = 5
[hand.permissions]
requires = [
"quiz.generate",
"quiz.grade",
"quiz.analyze"
]
roles = ["operator.read", "operator.write"]
[hand.rate_limit]
max_requests = 50
window_seconds = 3600
[hand.audit]
log_inputs = true
log_outputs = true
retention_days = 30
# 测验动作定义
[[hand.actions]]
id = "generate"
name = "生成测验"
description = "根据主题或内容生成测验题目"
params = { topic = "string", content = "string?", question_type = "string?", count = "number?", difficulty = "string?" }
[[hand.actions]]
id = "grade"
name = "评估答案"
description = "评估用户提交的答案"
params = { quiz_id = "string", answers = "array" }
[[hand.actions]]
id = "analyze"
name = "分析表现"
description = "分析用户的测验表现和学习进度"
params = { quiz_id = "string", user_id = "string?" }
[[hand.actions]]
id = "hint"
name = "提供提示"
description = "为当前题目提供提示"
params = { question_id = "string", hint_level = "number?" }
[[hand.actions]]
id = "explain"
name = "解释答案"
description = "提供题目的详细解析"
params = { question_id = "string" }
[[hand.actions]]
id = "adaptive_next"
name = "自适应下一题"
description = "根据当前表现推荐下一题难度"
params = { current_score = "number", questions_answered = "number" }
[[hand.actions]]
id = "generate_report"
name = "生成报告"
description = "生成测验结果报告"
params = { quiz_id = "string", format = "string?" }

119
hands/slideshow.HAND.toml Normal file
View File

@@ -0,0 +1,119 @@
# Slideshow Hand - 幻灯片控制能力包
#
# ZCLAW Hand 配置
# 提供幻灯片演示控制能力,支持翻页、聚焦、激光笔等
[hand]
name = "slideshow"
version = "1.0.0"
description = "幻灯片控制能力包 - 控制演示文稿的播放、导航和标注"
author = "ZCLAW Team"
type = "presentation"
requires_approval = false
timeout = 30
max_concurrent = 1
tags = ["slideshow", "presentation", "slides", "education", "teaching"]
[hand.config]
# 支持的幻灯片格式
supported_formats = ["pptx", "pdf", "html", "markdown"]
# 自动翻页间隔0 表示禁用
auto_advance_interval = 0
# 是否显示进度条
show_progress = true
# 是否显示页码
show_page_number = true
# 激光笔颜色
laser_color = "#ff0000"
# 聚焦框颜色
spotlight_color = "#ffcc00"
[hand.triggers]
manual = true
schedule = false
webhook = false
[[hand.triggers.events]]
type = "chat.intent"
pattern = "幻灯片|演示|翻页|下一页|上一页|slide|presentation|next|prev"
priority = 5
[hand.permissions]
requires = [
"slideshow.navigate",
"slideshow.annotate",
"slideshow.control"
]
roles = ["operator.read"]
[hand.rate_limit]
max_requests = 200
window_seconds = 3600
[hand.audit]
log_inputs = true
log_outputs = false
retention_days = 7
# 幻灯片动作定义
[[hand.actions]]
id = "next_slide"
name = "下一页"
description = "切换到下一张幻灯片"
params = {}
[[hand.actions]]
id = "prev_slide"
name = "上一页"
description = "切换到上一张幻灯片"
params = {}
[[hand.actions]]
id = "goto_slide"
name = "跳转到指定页"
description = "跳转到指定编号的幻灯片"
params = { slide_number = "number" }
[[hand.actions]]
id = "spotlight"
name = "聚焦元素"
description = "用高亮框聚焦指定元素"
params = { element_id = "string", duration = "number?" }
[[hand.actions]]
id = "laser"
name = "激光笔"
description = "在幻灯片上显示激光笔指示"
params = { x = "number", y = "number", duration = "number?" }
[[hand.actions]]
id = "highlight"
name = "高亮区域"
description = "高亮显示幻灯片上的区域"
params = { x = "number", y = "number", width = "number", height = "number", color = "string?" }
[[hand.actions]]
id = "play_animation"
name = "播放动画"
description = "触发幻灯片上的动画效果"
params = { animation_id = "string" }
[[hand.actions]]
id = "pause"
name = "暂停"
description = "暂停自动播放"
params = {}
[[hand.actions]]
id = "resume"
name = "继续"
description = "继续自动播放"
params = {}

127
hands/speech.HAND.toml Normal file
View File

@@ -0,0 +1,127 @@
# Speech Hand - 语音合成能力包
#
# ZCLAW Hand 配置
# 提供文本转语音 (TTS) 能力,支持多种语音和语言
[hand]
name = "speech"
version = "1.0.0"
description = "语音合成能力包 - 将文本转换为自然语音输出"
author = "ZCLAW Team"
type = "media"
requires_approval = false
timeout = 120
max_concurrent = 3
tags = ["speech", "tts", "voice", "audio", "education", "accessibility"]
[hand.config]
# TTS 提供商: browser, azure, openai, elevenlabs, local
provider = "browser"
# 默认语音
default_voice = "default"
# 默认语速 (0.5 - 2.0)
default_rate = 1.0
# 默认音调 (0.5 - 2.0)
default_pitch = 1.0
# 默认音量 (0 - 1.0)
default_volume = 1.0
# 语言代码
default_language = "zh-CN"
# 是否缓存音频
cache_audio = true
# Azure TTS 配置 (如果 provider = "azure")
[hand.config.azure]
# voice_name = "zh-CN-XiaoxiaoNeural"
# region = "eastasia"
# OpenAI TTS 配置 (如果 provider = "openai")
[hand.config.openai]
# model = "tts-1"
# voice = "alloy"
# 浏览器 TTS 配置 (如果 provider = "browser")
[hand.config.browser]
# 使用系统默认语音
use_system_voice = true
# 语音名称映射
voice_mapping = { "zh-CN" = "Microsoft Huihui", "en-US" = "Microsoft David" }
[hand.triggers]
manual = true
schedule = false
webhook = false
[[hand.triggers.events]]
type = "chat.intent"
pattern = "朗读|念|说|播放语音|speak|read|say|tts"
priority = 5
[hand.permissions]
requires = [
"speech.synthesize",
"speech.play",
"speech.stop"
]
roles = ["operator.read"]
[hand.rate_limit]
max_requests = 100
window_seconds = 3600
[hand.audit]
log_inputs = true
log_outputs = false # 音频不记录
retention_days = 3
# 语音动作定义
[[hand.actions]]
id = "speak"
name = "朗读文本"
description = "将文本转换为语音并播放"
params = { text = "string", voice = "string?", rate = "number?", pitch = "number?" }
[[hand.actions]]
id = "speak_ssml"
name = "朗读 SSML"
description = "使用 SSML 标记朗读文本(支持更精细控制)"
params = { ssml = "string", voice = "string?" }
[[hand.actions]]
id = "pause"
name = "暂停播放"
description = "暂停当前语音播放"
params = {}
[[hand.actions]]
id = "resume"
name = "继续播放"
description = "继续暂停的语音播放"
params = {}
[[hand.actions]]
id = "stop"
name = "停止播放"
description = "停止当前语音播放"
params = {}
[[hand.actions]]
id = "list_voices"
name = "列出可用语音"
description = "获取可用的语音列表"
params = { language = "string?" }
[[hand.actions]]
id = "set_voice"
name = "设置默认语音"
description = "更改默认语音设置"
params = { voice = "string", language = "string?" }

125
hands/whiteboard.HAND.toml Normal file
View File

@@ -0,0 +1,125 @@
# Whiteboard Hand - 白板绘制能力包
#
# ZCLAW Hand 配置
# 提供交互式白板绘制能力,支持文本、图形、公式、图表等
[hand]
name = "whiteboard"
version = "1.0.0"
description = "白板绘制能力包 - 绘制文本、图形、公式、图表等教学内容"
author = "ZCLAW Team"
type = "presentation"
requires_approval = false
timeout = 60
max_concurrent = 1
tags = ["whiteboard", "drawing", "presentation", "education", "teaching"]
[hand.config]
# 画布尺寸
canvas_width = 1920
canvas_height = 1080
# 默认画笔颜色
default_color = "#333333"
# 默认线宽
default_line_width = 2
# 支持的绘制动作
supported_actions = [
"draw_text",
"draw_shape",
"draw_line",
"draw_chart",
"draw_latex",
"draw_table",
"erase",
"clear",
"undo",
"redo"
]
# 字体配置
[hand.config.fonts]
text_font = "system-ui"
math_font = "KaTeX_Main"
code_font = "JetBrains Mono"
[hand.triggers]
manual = true
schedule = false
webhook = false
[[hand.triggers.events]]
type = "chat.intent"
pattern = "画|绘制|白板|展示|draw|whiteboard|sketch"
priority = 5
[hand.permissions]
requires = [
"whiteboard.draw",
"whiteboard.clear",
"whiteboard.export"
]
roles = ["operator.read"]
[hand.rate_limit]
max_requests = 100
window_seconds = 3600
[hand.audit]
log_inputs = true
log_outputs = false # 绘制内容不记录
retention_days = 7
# 绘制动作定义
[[hand.actions]]
id = "draw_text"
name = "绘制文本"
description = "在白板上绘制文本"
params = { x = "number", y = "number", text = "string", font_size = "number?", color = "string?" }
[[hand.actions]]
id = "draw_shape"
name = "绘制图形"
description = "绘制矩形、圆形、箭头等基本图形"
params = { shape = "string", x = "number", y = "number", width = "number", height = "number", fill = "string?" }
[[hand.actions]]
id = "draw_line"
name = "绘制线条"
description = "绘制直线或曲线"
params = { points = "array", color = "string?", line_width = "number?" }
[[hand.actions]]
id = "draw_chart"
name = "绘制图表"
description = "绘制柱状图、折线图、饼图等"
params = { chart_type = "string", data = "object", x = "number", y = "number", width = "number", height = "number" }
[[hand.actions]]
id = "draw_latex"
name = "绘制公式"
description = "渲染 LaTeX 数学公式"
params = { latex = "string", x = "number", y = "number", font_size = "number?" }
[[hand.actions]]
id = "draw_table"
name = "绘制表格"
description = "绘制数据表格"
params = { headers = "array", rows = "array", x = "number", y = "number" }
[[hand.actions]]
id = "clear"
name = "清空画布"
description = "清空白板所有内容"
params = {}
[[hand.actions]]
id = "export"
name = "导出图片"
description = "将白板内容导出为图片"
params = { format = "string?" }