chore: release v0.11.6

feat: Skills multi-agent support — agent selector + per-agent skills directory (Rust/Node.js/frontend)
feat: Assistant tool mode streaming — typewriter effect + tool_calls chunk accumulation
improve: OpenClaw 4.5 compatibility — full agent event stream handling + 3-min ultimate timeout
improve: Replace hot-update with stable download links (website/GitHub)
fix: Gateway status flapping — dashboard throttle + TCP retry + debounce threshold
fix: Assistant empty gray bubbles — SSE 0-chunk detection + stream error capture + render filter
This commit is contained in:
晴天
2026-04-07 16:17:09 +08:00
parent 35423f428b
commit 3c5a0d252b
35 changed files with 438 additions and 225 deletions

View File

@@ -899,7 +899,19 @@ function parseSkillFrontmatterFile(skillMdPath) {
}
}
function collectLocalSkillRoots() {
function resolveAgentSkillsDir(agentId) {
const id = (agentId || '').trim()
if (!id || id === 'main') return null
try {
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'))
const ws = resolveAgentWorkspace(config, id)
return path.join(ws, 'skills')
} catch {
return path.join(OPENCLAW_DIR, 'agents', id, 'workspace', 'skills')
}
}
function collectLocalSkillRoots(agentSkillsDir) {
const roots = []
const seen = new Set()
const pushRoot = (dir, source, bundled = false) => {
@@ -911,7 +923,11 @@ function collectLocalSkillRoots() {
roots.push({ dir: normalized, source, bundled })
}
pushRoot(path.join(OPENCLAW_DIR, 'skills'), 'OpenClaw 自定义', false)
if (agentSkillsDir) {
pushRoot(agentSkillsDir, 'Agent 自定义', false)
} else {
pushRoot(path.join(OPENCLAW_DIR, 'skills'), 'OpenClaw 自定义', false)
}
pushRoot(path.join(homedir(), '.claude', 'skills'), 'Claude 自定义', false)
const cliPath = resolveOpenclawCliPath()
@@ -979,8 +995,8 @@ function scanSingleSkill(root, name) {
return result
}
function scanLocalSkillsFallback(cliError = null) {
const roots = collectLocalSkillRoots()
function scanLocalSkillsFallback(agentSkillsDir = null) {
const roots = collectLocalSkillRoots(agentSkillsDir)
const skills = []
const seen = new Set()
const scannedRoots = []
@@ -5350,12 +5366,14 @@ const handlers = {
},
// Skills 管理(纯本地扫描,不依赖 CLI
skills_list() {
return scanLocalSkillsFallback()
skills_list({ agent_id } = {}) {
const agentDir = resolveAgentSkillsDir(agent_id)
return scanLocalSkillsFallback(agentDir)
},
skills_info({ name }) {
skills_info({ name, agent_id } = {}) {
const n = String(name || '').trim()
const fallback = scanLocalSkillsFallback().skills.find(skill => skill.name === n)
const agentDir = resolveAgentSkillsDir(agent_id)
const fallback = scanLocalSkillsFallback(agentDir).skills.find(skill => skill.name === n)
if (fallback) return fallback
throw new Error(`Skill「${n}」不存在`)
},
@@ -5384,9 +5402,11 @@ const handlers = {
throw new Error(`安装失败: ${e.message || e}`)
}
},
skills_uninstall({ name }) {
skills_uninstall({ name, agent_id } = {}) {
if (!name || name.includes('..') || name.includes('/') || name.includes('\\')) throw new Error('无效的 Skill 名称')
const skillDir = path.join(OPENCLAW_DIR, 'skills', name)
const agentDir = resolveAgentSkillsDir(agent_id)
const baseDir = agentDir || path.join(OPENCLAW_DIR, 'skills')
const skillDir = path.join(baseDir, name)
if (!fs.existsSync(skillDir)) throw new Error(`Skill「${name}」不存在`)
fs.rmSync(skillDir, { recursive: true, force: true })
return { success: true, name }
@@ -5398,8 +5418,9 @@ const handlers = {
async skillhub_index() {
return await skillhubSdk.fetchIndex()
},
async skillhub_install({ slug }) {
const skillsDir = path.join(OPENCLAW_DIR, 'skills')
async skillhub_install({ slug, agent_id } = {}) {
const agentDir = resolveAgentSkillsDir(agent_id)
const skillsDir = agentDir || path.join(OPENCLAW_DIR, 'skills')
if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true })
const installedPath = await skillhubSdk.install(slug, skillsDir)
return { success: true, slug, path: installedPath }