Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions MicroClawDeployer.spec
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- mode: python ; coding: utf-8 -*-

import importlib
from pathlib import Path

from PyInstaller.utils.hooks import collect_data_files, collect_dynamic_libs

Expand All @@ -20,6 +21,11 @@ if importlib.util.find_spec('webview') is None:
webview_datas = collect_data_files('webview', subdir='lib') + collect_data_files('webview', subdir='js')
webview_binaries = collect_dynamic_libs('webview')
pythonnet_datas = collect_data_files('pythonnet', subdir='runtime')
managed_skill_datas = [
(str(skill_dir), f"skills/{skill_dir.name}")
for skill_dir in Path("skills").iterdir()
if skill_dir.is_dir() and skill_dir.name != "rednote-publisher"
]

a = Analysis(
['deploy.py'],
Expand All @@ -29,10 +35,9 @@ a = Analysis(
('dist/microclaw-portable.zip', '.'),
('dist/install-manifest.json', '.'),
('scripts/windows/setup-dependencies.ps1', '.'),
('skills', 'skills'),
('scripts', 'scripts'),
('deployer/assets', 'deployer/assets'),
] + webview_datas + pythonnet_datas,
] + managed_skill_datas + webview_datas + pythonnet_datas,
hiddenimports=[],
hookspath=[],
hooksconfig={},
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ MicroClaw is designed to remove the usual Windows setup friction around OpenClaw
- **Only the LLM is bring-your-own**: the user supplies the model endpoint, API key, and model name; the rest of the stack is installed and configured by MicroClaw
- **Fast path on first launch**: if no provider is configured, the app opens a setup wizard that asks only for model credentials; if `MODEL_*` values already exist in `.env`, MicroClaw can auto-configure them
- **Recommended tasks on day one**: the home screen ships with starter task cards and prompt suggestions so users can begin with concrete tasks instead of a blank chat box
- **Preloaded capability surface**: 52 bundled skills plus 6 managed skills are available as part of the default Windows experience
- **Preloaded capability surface**: 52 bundled skills plus 6 managed skills are available as part of the default Windows experience; specialist agents add their own skills on demand

### Built-In Trust

Expand Down Expand Up @@ -235,6 +235,15 @@ Installed to `~/.openclaw/skills/`, these are custom advanced skills included in
| desktop-organizer | Scan and organize files on the Windows desktop |
| security-practice | AI agent safety practices (red/yellow line rules, install audit protocol) |

### Agent-Owned Skills

Agent-owned skills are dormant resources in the desktop package and are installed only
when their owning specialist agent is added:

| Agent | Skill | Description |
|---|---|---|
| Creative Muse | rednote-publisher | Create staged topic ideas, sourced material kits, and publish-ready Rednote packages |

---

## WeChat Plugin
Expand Down
10 changes: 9 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ MicroClaw 的设计重点,是把 OpenClaw 在 Windows 上常见的安装和配
- **唯一需要用户自备的是 LLM**:用户提供模型地址、API Key 和模型名,其余栈内组件由 MicroClaw 安装并连接起来
- **首启路径很短**:如果还没有模型配置,应用只会引导填写模型凭据;如果 `.env` 里已有 `MODEL_*`,MicroClaw 还可以自动配置
- **首屏自带推荐任务**:首页不是空白输入框,而是预置了任务卡片和提示词建议,安装后就能直接开始做事
- **能力预装到位**:52 个内置技能加 6 个托管技能,作为默认 Windows 体验的一部分直接可用
- **能力预装到位**:52 个内置技能加 6 个托管技能默认可用;专家智能体按需安装自己的专属技能

### 内建信任

Expand Down Expand Up @@ -150,6 +150,14 @@ npm run dev
| desktop-organizer | 扫描并整理 Windows 桌面文件 |
| security-practice | AI Agent 行为安全规范(红线/黄线规则、安装审计协议) |

### 智能体专属技能

专属技能只作为桌面安装包中的休眠资源存在;添加对应专家智能体时才会安装:

| 智能体 | 技能 | 说明 |
|---|---|---|
| 灵感创客 | rednote-publisher | 生成分阶段选题、素材集合和可发布的小红书图文包 |

---

## 微信插件
Expand Down
File renamed without changes
2 changes: 1 addition & 1 deletion deployer/webview_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ def get_bootstrap(self):
"lawyer.png",
"painter.png",
"scientist.png",
"singer.png",
"creative-muse.png",
]
with self._state_lock:
install_dir = self._state["install_dir"]
Expand Down
14 changes: 7 additions & 7 deletions deployer/windows_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3239,16 +3239,16 @@ def write_config(self) -> bool:
# on the whitelist via entries.<name>.enabled=false in openclaw.json.
# An empty allowManaged list with enable=true disables ALL managed/workspace skills.
if allow_managed is not None:
from deployer.skill_catalog import MANAGED_SKILL_CATALOG

entries = skills_cfg.get("entries", {})
# Skills in the whitelist: ensure enabled=true.
for name in allow_managed:
# The installer owns only its managed catalog. Preserve entries
# created by the desktop app or by the user.
tracked_names = set(MANAGED_SKILL_CATALOG.keys()) | set(allow_managed)
for name in tracked_names:
entry = entries.get(name, {})
entry["enabled"] = True
entry["enabled"] = name in allow_managed
entries[name] = entry
# Skills already tracked in entries that are NOT in the whitelist: disable.
for name in list(entries.keys()):
if name not in allow_managed:
entries[name]["enabled"] = False
skills_cfg["entries"] = entries

existing["skills"] = skills_cfg
Expand Down
2 changes: 2 additions & 0 deletions desktop/electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ extraResources:
to: sandbox-sensitive.js
- from: ../appcontainer/path-extraction.js
to: path-extraction.js
- from: ../skills/rednote-publisher/
to: agent-skills/rednote-publisher/

files:
- dist/**/*
Expand Down
4 changes: 2 additions & 2 deletions desktop/renderer/src/components/GatewayLoading.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ import imgGeologist from "@/assets/Geologist.png";
import imgLawyer from "@/assets/Lawyer.png";
import imgPainter from "@/assets/Painter.png";
import imgScientist from "@/assets/Scientist.png";
import imgSinger from "@/assets/Singer.png";
import imgCreativeMuse from "@/assets/CreativeMuse.png";

const IDENTITY_IMAGES = [
imgArchaeologist,
Expand All @@ -134,7 +134,7 @@ const IDENTITY_IMAGES = [
imgLawyer,
imgPainter,
imgScientist,
imgSinger,
imgCreativeMuse,
];

const CAROUSEL_KEYS = [
Expand Down
27 changes: 15 additions & 12 deletions desktop/renderer/src/i18n/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,18 +835,21 @@ export default {
"agent.leopard.task.3.desc":
"Provide risk diversification optimization based on current holdings.",

"agent.singer.name": "Singer-Songwriter",
"agent.singer.desc": "Lyrics, arrangement inspiration & music style analysis",
"agent.singer.tag.1": "Lyrics Writing",
"agent.singer.tag.2": "Arrangement Ideas",
"agent.singer.tag.3": "Style Analysis",
"agent.singer.task.1.title": "Write some lyrics for me",
"agent.singer.task.1.desc": "Create original lyrics based on theme and emotion.",
"agent.singer.task.2.title": "Analyze this song's style",
"agent.singer.task.2.desc": "Break down genre, arrangement techniques, and emotional expression.",
"agent.singer.task.3.title": "Recommend arrangement inspiration",
"agent.singer.task.3.desc":
"Suggest reference tracks and sound combinations by style preference.",
"agent.creativeMuse.name": "Creative Muse",
"agent.creativeMuse.desc":
"A Rednote studio from topic ideas and material kits to publish-ready visual packages",
"agent.creativeMuse.tag.1": "Topic Discovery",
"agent.creativeMuse.tag.2": "Material Kits",
"agent.creativeMuse.tag.3": "Visual Packages",
"agent.creativeMuse.task.1.title": "Find Rednote topic ideas",
"agent.creativeMuse.task.1.desc":
"Research recent discussions, list 5 specific themes, and recommend the strongest angle.",
"agent.creativeMuse.task.2.title": "Build a material kit for the selected idea",
"agent.creativeMuse.task.2.desc":
"Collect facts, sources, keywords, an outline, and cover and visual directions.",
"agent.creativeMuse.task.3.title": "Create a Rednote package from the material kit",
"agent.creativeMuse.task.3.desc":
"Generate complete copy, titles, hashtags, a cover, and carousel visual cards.",

"agent.main.task.1.title": "Summarize this content",
"agent.main.task.1.desc": "Quickly extract key points from articles or documents.",
Expand Down
8 changes: 8 additions & 0 deletions desktop/renderer/src/i18n/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ describe("i18n", () => {
setLocale("en-US"); // restore
});

it("Creative Muse catalog keys exist in both locales", () => {
setLocale("en-US");
expect(t("agent.creativeMuse.name")).toBe("Creative Muse");
setLocale("zh-CN");
expect(t("agent.creativeMuse.name")).toBe("灵感创客");
setLocale("en-US"); // restore
});

it("localizes Agent Team and Agent Catalog navigation in zh-CN", () => {
setLocale("zh-CN");
expect(t("sidebar.agents")).toBe("智能体团队");
Expand Down
22 changes: 11 additions & 11 deletions desktop/renderer/src/i18n/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,17 +791,17 @@ export default {
"agent.leopard.task.3.title": "仓位调整建议",
"agent.leopard.task.3.desc": "根据当前持仓给出风险分散优化方案。",

"agent.singer.name": "创作歌手",
"agent.singer.desc": "歌词创作、编曲灵感与音乐风格分析",
"agent.singer.tag.1": "歌词创作",
"agent.singer.tag.2": "编曲灵感",
"agent.singer.tag.3": "风格分析",
"agent.singer.task.1.title": "帮我写一段歌词",
"agent.singer.task.1.desc": "根据主题和情感创作原创歌词段落。",
"agent.singer.task.2.title": "分析这首歌的风格",
"agent.singer.task.2.desc": "拆解曲风、编曲手法和情绪表达特点。",
"agent.singer.task.3.title": "推荐编曲灵感",
"agent.singer.task.3.desc": "根据风格偏好推荐参考曲目和音色搭配。",
"agent.creativeMuse.name": "灵感创客",
"agent.creativeMuse.desc": "从选题、素材集合到图文发布包的小红书内容工作室",
"agent.creativeMuse.tag.1": "选题发现",
"agent.creativeMuse.tag.2": "素材整理",
"agent.creativeMuse.tag.3": "图文发布包",
"agent.creativeMuse.task.1.title": "寻找小红书选题灵感",
"agent.creativeMuse.task.1.desc": "搜索近期讨论,枚举 5 个具体主题,并推荐最值得继续的切入点。",
"agent.creativeMuse.task.2.title": "为选中主题整理素材集合",
"agent.creativeMuse.task.2.desc": "汇总事实、来源、关键词、内容结构和封面及配图方向。",
"agent.creativeMuse.task.3.title": "从素材集合生成小红书发布包",
"agent.creativeMuse.task.3.desc": "生成完整正文、标题、标签、封面和轮播图文卡片。",

"agent.main.task.1.title": "帮我总结这段内容",
"agent.main.task.1.desc": "快速提炼文章或文档的核心要点。",
Expand Down
28 changes: 28 additions & 0 deletions desktop/renderer/src/stores/agents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ describe("agent store", () => {
expect(store.agents.map((agent) => agent.id)).toEqual(["main", "custom"]);
});

it("hides the Singer compatibility alias from the sidebar", async () => {
const store = useAgentStore();
agentsList.mockResolvedValueOnce({
agents: [
{ id: "main", name: "Assistant" },
{ id: "creative-muse", name: "Creative Muse" },
{ id: "singer", name: "Creative Muse" },
],
});

await store.fetchAgents();

expect(store.agents.map((agent) => agent.id)).toEqual(["main", "creative-muse"]);
});

it("adds Master Archive to OpenClaw before showing it in the sidebar", async () => {
const store = useAgentStore();
const marketAgent = store.marketAgents.find((entry) => entry.id === "master-archive");
Expand Down Expand Up @@ -85,6 +100,19 @@ describe("agent store", () => {
expect(store.marketAgents.some((entry) => entry.id === "growth-hacker")).toBe(false);
});

it("presents the Creative Muse platform content scenarios", () => {
const store = useAgentStore();

expect(store.marketAgents.find((entry) => entry.id === "creative-muse")).toMatchObject({
name: "Creative Muse",
description:
"A Rednote studio from topic ideas and material kits to publish-ready visual packages",
tags: ["Topic Discovery", "Material Kits", "Visual Packages"],
isAdded: false,
});
expect(store.marketAgents.some((entry) => entry.id === "singer")).toBe(false);
});

it("keeps the roster unchanged when Add fails", async () => {
const store = useAgentStore();
agentsAdd.mockRejectedValueOnce(new Error("Gateway restart failed"));
Expand Down
7 changes: 6 additions & 1 deletion desktop/renderer/src/stores/agents.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { t, locale } from "../i18n";
import { AGENT_CATALOG, type AgentCatalogEntry } from "../../../src/agent-catalog";
import {
AGENT_CATALOG,
LEGACY_AGENT_ID_ALIASES,
type AgentCatalogEntry,
} from "../../../src/agent-catalog";

export interface QuickTask {
title: string;
Expand Down Expand Up @@ -64,6 +68,7 @@ function normalizeRuntimeAgents(value: unknown): RuntimeAgent[] {
return [];
}
const id = (candidate as { id: string }).id;
if (Object.hasOwn(LEGACY_AGENT_ID_ALIASES, id)) return [];
const nameValue = (candidate as { name?: unknown }).name;
const descriptionValue = (candidate as { description?: unknown }).description;
return [
Expand Down
7 changes: 7 additions & 0 deletions desktop/renderer/src/stores/chat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,13 @@ describe("useChatStore — draft sessions", () => {
expect(store.sessionKey).toMatch(/^session-/);
expect(store.currentSessionAgentId).toBe("main");
});

it("attributes historical Singer session keys to Creative Muse", () => {
const store = useChatStore();
store.newSession("singer");
expect(store.sessionKey).toMatch(/^agent:singer:session-/);
expect(store.currentSessionAgentId).toBe("creative-muse");
});
});

describe("useChatStore — attachments", () => {
Expand Down
6 changes: 3 additions & 3 deletions desktop/renderer/src/stores/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ref, computed } from "vue";
import { useSessionStore } from "./sessions";
import { redactPii } from "@/utils/pii-scanner";
import { hydratePrivacyControls, privacyControlsToScanOptions } from "@/utils/privacy-settings";
import { canonicalAgentId } from "../../../src/agent-catalog";

/**
* Chat store — mirrors the webchat gateway protocol.
Expand Down Expand Up @@ -197,7 +198,7 @@ export const useChatStore = defineStore("chat", () => {
*/
const currentSessionAgentId = computed(() => {
const match = /^agent:([^:]+):/.exec(sessionKey.value);
if (match) return match[1];
if (match) return canonicalAgentId(match[1]);
return pendingSessionAgentId.value || "main";
});

Expand Down Expand Up @@ -1091,8 +1092,7 @@ export const useChatStore = defineStore("chat", () => {

const { phase, name, toolCallId, meta } = payload.data;
const args = (payload.data as Record<string, unknown>).args as
| Record<string, unknown>
| undefined;
Record<string, unknown> | undefined;
if (phase === "start") {
// Build a descriptive display name combining tool name + primary argument
let displayName = name;
Expand Down
22 changes: 22 additions & 0 deletions desktop/renderer/src/stores/sessions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,28 @@ describe("useSessionStore", () => {
expect(store.sessions[0].key).toBe("loaded");
});

it("keeps historical Singer sessions visible under Creative Muse", () => {
storage["openclaw-sessions"] = JSON.stringify([
{
key: "agent:singer:legacy",
title: "Legacy draft",
createdAt: 1,
updatedAt: 1,
preview: "lyrics",
agentId: "singer",
},
]);
setActivePinia(createPinia());

const store = useSessionStore();

expect(store.sessions[0]).toMatchObject({
key: "agent:singer:legacy",
agentId: "creative-muse",
title: "Legacy draft",
});
});

it("removes a persisted reserved warm-up session on load", () => {
storage["openclaw-sessions"] = JSON.stringify([
{
Expand Down
Loading
Loading