create-openclaw-bot 5.13.6 → 5.13.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/README.vi.md +11 -3
- package/dist/server/local-server.js +44 -29
- package/dist/setup/shared/bot-config-gen.js +8 -0
- package/dist/setup/shared/docker-gen.js +12 -0
- package/dist/setup/shared/workspace-gen.js +2 -2
- package/dist/web/app.js +2 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*Run one command → open the dashboard → your bot is live. Windows · macOS · Linux · VPS — Docker-powered, auto-installed for you.*
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.13.
|
|
10
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.13.9-0EA5E9?style=for-the-badge" alt="Version 5.13.9" /></a>
|
|
11
11
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
12
12
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
13
13
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -37,11 +37,19 @@
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
-
## 🆕 What's New in v5.13.
|
|
40
|
+
## 🆕 What's New in v5.13.9
|
|
41
|
+
|
|
42
|
+
- 🧠 **Always-on memory for every bot (learning-memory)**: a new context-engine plugin injects your curated `MEMORY.md` + `USER.md` into **every** turn — including group chats, which the default recall skips. Bots stop forgetting context and rules over time: no more chiming in when nobody called them, or re-asking things you already told them. Auto-installed for new bots + a 1-click card in the dashboard.
|
|
43
|
+
- 🧹 **Retired TencentDB Agent Memory** in favor of the always-on engine above (no external service needed). Existing bots are auto-migrated on update — the old learning-memory *skill* and the TencentDB plugin are removed automatically.
|
|
44
|
+
|
|
45
|
+
<details>
|
|
46
|
+
<summary><b>Previous: What's new in v5.13.4</b></summary>
|
|
41
47
|
|
|
42
48
|
- ⚙️ **New Settings page**: pick your theme (light/dark toggle), language (VI/EN) and timezone right from the dashboard — available in both the sidebar and the mobile nav bar.
|
|
43
49
|
- 🕒 **Schedules & cron run in the correct local time**: newly created bots carry an explicit timezone, so reminders/cron no longer drift a day off around midnight (a UTC-vs-local bug that could schedule into the past). Scheduling guidance was hardened end-to-end (local time + timezone, explicit delivery target, raw group id).
|
|
44
50
|
|
|
51
|
+
</details>
|
|
52
|
+
|
|
45
53
|
<details>
|
|
46
54
|
<summary><b>Previous: What's new in v5.13.0</b></summary>
|
|
47
55
|
|
|
@@ -336,7 +344,7 @@ Companion repos to build a complete, self-running AI assistant:
|
|
|
336
344
|
- [openclaw-n8n-facebook-poster](https://github.com/tuanminhhole/openclaw-n8n-facebook-poster) — Auto-post to Facebook via n8n
|
|
337
345
|
|
|
338
346
|
**🧩 Skills**
|
|
339
|
-
- [openclaw-
|
|
347
|
+
- [openclaw-learning-memory](https://github.com/tuanminhhole/openclaw-learning-memory) — Always-on memory context engine (injects curated MEMORY.md + USER.md into every turn, incl. groups)
|
|
340
348
|
- [openclaw-skill-infographic](https://github.com/tuanminhhole/openclaw-skill-infographic) — Infographic generation
|
|
341
349
|
|
|
342
350
|
---
|
package/README.vi.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*Chạy một lệnh → mở dashboard → bot lên sóng. Windows · macOS · Linux · VPS — chạy trên Docker, tự cài giúp bạn.*
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.13.
|
|
10
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.13.9-0EA5E9?style=for-the-badge" alt="Version 5.13.9" /></a>
|
|
11
11
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
12
12
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
13
13
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -37,11 +37,19 @@
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
-
## 🆕 Có gì mới trong v5.13.
|
|
40
|
+
## 🆕 Có gì mới trong v5.13.9
|
|
41
|
+
|
|
42
|
+
- 🧠 **Trí nhớ always-on cho mọi bot (learning-memory)**: plugin context-engine mới nạp `MEMORY.md` + `USER.md` đã chắt lọc vào **MỌI lượt** — kể cả trong nhóm (điều recall mặc định bỏ qua). Bot hết quên ngữ cảnh/quy tắc theo thời gian: không còn "nói leo" khi không ai gọi, không hỏi lại điều bạn đã dặn. Tự cài cho bot mới + có card 1-chạm trên dashboard.
|
|
43
|
+
- 🧹 **Bỏ TencentDB Agent Memory**, thay bằng engine always-on ở trên (không cần dịch vụ ngoài). Bot cũ tự migrate khi cập nhật — *skill* learning-memory cũ và plugin TencentDB được gỡ tự động.
|
|
44
|
+
|
|
45
|
+
<details>
|
|
46
|
+
<summary><b>Trước đó: Có gì mới trong v5.13.4</b></summary>
|
|
41
47
|
|
|
42
48
|
- ⚙️ **Trang Cài đặt mới**: chọn giao diện (nút gạt Sáng/Tối), ngôn ngữ (VI/EN) và múi giờ ngay trong dashboard — có ở cả sidebar lẫn thanh điều hướng mobile.
|
|
43
49
|
- 🕒 **Lịch & cron chạy đúng giờ địa phương**: bot tạo mới nhận múi giờ rõ ràng, nên nhắc hẹn/cron không còn lệch 1 ngày quanh nửa đêm (lỗi UTC-vs-local có thể đặt lịch vào quá khứ). Đã siết hướng dẫn lập lịch toàn diện (giờ local + múi giờ, nơi gửi rõ ràng, ID nhóm dạng thô).
|
|
44
50
|
|
|
51
|
+
</details>
|
|
52
|
+
|
|
45
53
|
<details>
|
|
46
54
|
<summary><b>Trước đó: Có gì mới trong v5.13.0</b></summary>
|
|
47
55
|
|
|
@@ -336,7 +344,7 @@ Các repo đi kèm để bạn dựng một trợ lý AI "tự vận hành" hoà
|
|
|
336
344
|
- [openclaw-n8n-facebook-poster](https://github.com/tuanminhhole/openclaw-n8n-facebook-poster) — Tự động đăng Facebook qua n8n
|
|
337
345
|
|
|
338
346
|
**🧩 Skill**
|
|
339
|
-
- [openclaw-
|
|
347
|
+
- [openclaw-learning-memory](https://github.com/tuanminhhole/openclaw-learning-memory) — Context engine trí nhớ always-on (nạp MEMORY.md + USER.md vào mọi lượt, kể cả nhóm)
|
|
340
348
|
- [openclaw-skill-infographic](https://github.com/tuanminhhole/openclaw-skill-infographic) — Tạo infographic
|
|
341
349
|
|
|
342
350
|
---
|
|
@@ -1032,14 +1032,16 @@ function ensureZaloModPluginConfig(entry, cfg) {
|
|
|
1032
1032
|
const gwPort = Number(cfg.gateway?.port) || state.gatewayPort || 18789;
|
|
1033
1033
|
entry.config.dashboardPort = gwPort + 1;
|
|
1034
1034
|
}
|
|
1035
|
-
//
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1035
|
+
// Seed the default bot's identity under bots.default (per-bot shape). zalo-mod
|
|
1036
|
+
// treats bots.<profile> as the canonical source — do NOT write legacy top-level
|
|
1037
|
+
// botName/zaloDisplayNames (they get stripped by the plugin's normalizer anyway).
|
|
1038
|
+
const firstAgentName = cfg.agents?.list?.[0]?.name;
|
|
1039
|
+
if (firstAgentName) {
|
|
1040
|
+
entry.config.bots = entry.config.bots || {};
|
|
1041
|
+
const def = entry.config.bots.default = entry.config.bots.default || {};
|
|
1042
|
+
if (!def.botName) def.botName = firstAgentName;
|
|
1043
|
+
if (!Array.isArray(def.zaloDisplayNames) || def.zaloDisplayNames.length === 0) def.zaloDisplayNames = [firstAgentName];
|
|
1044
|
+
if (!def.slashPrefix) def.slashPrefix = String(firstAgentName).toLowerCase().replace(/[^a-z0-9-]/g, '') || 'bot';
|
|
1043
1045
|
}
|
|
1044
1046
|
}
|
|
1045
1047
|
|
|
@@ -1465,12 +1467,19 @@ async function createBotInProject(projectDir, body = {}, runtime = {}) {
|
|
|
1465
1467
|
cfg.bindings.push({ agentId, match: { channel: 'telegram', accountId } });
|
|
1466
1468
|
await appendEnvValue(projectDir, accountId === 'default' ? 'TELEGRAM_BOT_TOKEN' : `TELEGRAM_BOT_TOKEN_${agentId.toUpperCase().replace(/[^A-Z0-9]+/g, '_')}`, token);
|
|
1467
1469
|
} else if (channel === 'zalo-personal') {
|
|
1468
|
-
const existingZaloConnectBindings = cfg.bindings.filter((b) => b.match?.channel === 'zalo-connect').length;
|
|
1469
|
-
if (existingZaloConnectBindings > 0) {
|
|
1470
|
-
throw httpError(400, 'OpenClaw Zalo Connect hiện hỗ trợ 1 tài khoản Zalo mỗi project. Hãy dùng bot Zalo hiện có hoặc tạo project riêng cho tài khoản thứ hai.');
|
|
1471
|
-
}
|
|
1472
1470
|
ensureZaloConnectChannel(cfg);
|
|
1473
|
-
|
|
1471
|
+
// zalo-connect (fork ≥3.0) is genuinely multi-account: API clients, stored
|
|
1472
|
+
// credentials and QR login are all keyed by accountId. So multiple Zalo
|
|
1473
|
+
// numbers can coexist in one project (mirrors Telegram): the first Zalo bot
|
|
1474
|
+
// uses 'default', each additional one gets its own account keyed by agentId.
|
|
1475
|
+
// Each new account still needs its own QR login (startZaloLogin reads the
|
|
1476
|
+
// accountId from this binding).
|
|
1477
|
+
const existingZaloConnectBindings = cfg.bindings.filter((b) => b.match?.channel === 'zalo-connect').length;
|
|
1478
|
+
accountId = existingZaloConnectBindings === 0 ? 'default' : agentId;
|
|
1479
|
+
const zc = cfg.channels['zalo-connect'];
|
|
1480
|
+
zc.accounts = zc.accounts || {};
|
|
1481
|
+
zc.accounts[accountId] = zc.accounts[accountId] || { enabled: true };
|
|
1482
|
+
zc.defaultAccount = zc.defaultAccount || 'default';
|
|
1474
1483
|
cfg.bindings.push({ agentId, match: { channel: 'zalo-connect', accountId } });
|
|
1475
1484
|
} else if (channel === 'fb-messenger') {
|
|
1476
1485
|
// Token handling (user token → permanent Page token) is done by the fb-messenger
|
|
@@ -3119,8 +3128,8 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
3119
3128
|
// from <workspace>/skills, so each bot's copy is independent. Toggling here affects ONLY the
|
|
3120
3129
|
// active bot's workspace folder. The global skills.entries[id].enabled flag is kept true
|
|
3121
3130
|
// while ANY bot still has the folder (openclaw needs it true to load the skill at all).
|
|
3122
|
-
if (kind === 'skill' &&
|
|
3123
|
-
const slugMap = { 'image-gen': 'infographic-generator'
|
|
3131
|
+
if (kind === 'skill' && id === 'image-gen') {
|
|
3132
|
+
const slugMap = { 'image-gen': 'infographic-generator' };
|
|
3124
3133
|
const slug = slugMap[id];
|
|
3125
3134
|
const rel = workspaceRelForAgent(agent, cfg, projectDir) || `workspace-${agent.id}`;
|
|
3126
3135
|
const folder = join(projectDir, '.openclaw', rel, 'skills', slug);
|
|
@@ -3277,7 +3286,9 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
3277
3286
|
// Most plugins are ClawHub packages (installed as `clawhub:<id>`). A few ship as plain npm
|
|
3278
3287
|
// packages and need their real package spec instead. Map id → install spec here.
|
|
3279
3288
|
const PLUGIN_NPM_SPEC = {
|
|
3280
|
-
|
|
3289
|
+
// learning-memory ships on ClawHub as `openclaw-learning-memory` (manifest id is
|
|
3290
|
+
// `learning-memory`, used as the config key). Install by the full package spec.
|
|
3291
|
+
'learning-memory': 'clawhub:openclaw-learning-memory',
|
|
3281
3292
|
};
|
|
3282
3293
|
const pluginInstallSpec = (id) => PLUGIN_NPM_SPEC[id] || `clawhub:${id}`;
|
|
3283
3294
|
|
|
@@ -3285,7 +3296,6 @@ async function installFeature(projectDir, agentId, kind, id) {
|
|
|
3285
3296
|
if (kind === 'skill') {
|
|
3286
3297
|
const skillSlugMap = {
|
|
3287
3298
|
'image-gen': 'infographic-generator',
|
|
3288
|
-
'learning-memory': 'learning-memory',
|
|
3289
3299
|
};
|
|
3290
3300
|
const slug = skillSlugMap[id] || id;
|
|
3291
3301
|
|
|
@@ -3372,6 +3382,11 @@ async function installFeature(projectDir, agentId, kind, id) {
|
|
|
3372
3382
|
sendLog('[zalo-connect] Restarting container to apply...');
|
|
3373
3383
|
await run('docker', ['restart', getBotContainerName(projectDir)], { shell: false }).catch(() => {});
|
|
3374
3384
|
}
|
|
3385
|
+
// Drop cached container extension versions so the card reflects the just-installed
|
|
3386
|
+
// version immediately (extver cache lives until explicitly cleared, like the
|
|
3387
|
+
// general install path below does).
|
|
3388
|
+
probeCacheClear(`extver:${projectDir}`);
|
|
3389
|
+
probeCacheClear(`runtime:${projectDir}`);
|
|
3375
3390
|
return { ok: true, id: 'zalo-connect' };
|
|
3376
3391
|
}
|
|
3377
3392
|
const installSpec = pluginInstallSpec(id);
|
|
@@ -3668,14 +3683,13 @@ async function getFeatureFlags(projectDir, agentId = '') {
|
|
|
3668
3683
|
// THIS agent's workspace (not the global skills.entries flag, which would leak across bots).
|
|
3669
3684
|
const imageGenOn = isSkillFolderExists(projectDir, aid, 'infographic-generator', cfg);
|
|
3670
3685
|
const webSearchOn = isEnabled(['duckduckgo']);
|
|
3671
|
-
const learningMemoryOn = isSkillFolderExists(projectDir, aid, 'learning-memory', cfg);
|
|
3672
3686
|
const aliases = {
|
|
3673
3687
|
browser: ['openclaw-browser-automation', 'browser-automation'],
|
|
3674
3688
|
zalo: ['openclaw-zalo-mod', 'zalo-mod'],
|
|
3675
3689
|
crawler: ['openclaw-facebook-crawler', 'openclaw-n8n-facebook-crawler', 'n8n-facebook-crawler'],
|
|
3676
3690
|
poster: ['openclaw-n8n-facebook-poster', 'openclaw-facebook-poster', 'facebook-poster'],
|
|
3677
3691
|
fbMessenger: ['openclaw-fb-messenger', 'fb-messenger'],
|
|
3678
|
-
|
|
3692
|
+
learningMemory: ['learning-memory', 'openclaw-learning-memory'],
|
|
3679
3693
|
zaloConnect: ['zalo-connect', 'openclaw-zalo-connect'],
|
|
3680
3694
|
};
|
|
3681
3695
|
const flags = {
|
|
@@ -3683,13 +3697,12 @@ async function getFeatureFlags(projectDir, agentId = '') {
|
|
|
3683
3697
|
'skill:cron': cronOn,
|
|
3684
3698
|
'skill:image-gen': imageGenOn,
|
|
3685
3699
|
'skill:web-search': webSearchOn,
|
|
3686
|
-
'skill:learning-memory': learningMemoryOn,
|
|
3687
3700
|
'plugin:openclaw-browser-automation': isEnabled(aliases.browser),
|
|
3688
3701
|
'plugin:openclaw-zalo-mod': isEnabled(aliases.zalo),
|
|
3689
3702
|
'plugin:openclaw-facebook-crawler': isEnabled(aliases.crawler),
|
|
3690
3703
|
'plugin:openclaw-n8n-facebook-poster': isEnabled(aliases.poster),
|
|
3691
3704
|
'plugin:openclaw-fb-messenger': isEnabled(aliases.fbMessenger),
|
|
3692
|
-
'plugin:memory
|
|
3705
|
+
'plugin:learning-memory': isEnabled(aliases.learningMemory),
|
|
3693
3706
|
'plugin:zalo-connect': isEnabled(aliases.zaloConnect),
|
|
3694
3707
|
};
|
|
3695
3708
|
const extensionsDir = join(projectDir || '', '.openclaw', 'extensions');
|
|
@@ -3699,7 +3712,6 @@ async function getFeatureFlags(projectDir, agentId = '') {
|
|
|
3699
3712
|
extensionDirExists(aliases) || isInstalledByRecord(aliases);
|
|
3700
3713
|
const installed = {
|
|
3701
3714
|
'skill:image-gen': isSkillFolderExists(projectDir, aid, 'infographic-generator', cfg),
|
|
3702
|
-
'skill:learning-memory': isSkillFolderExists(projectDir, aid, 'learning-memory', cfg),
|
|
3703
3715
|
'plugin:openclaw-browser-automation': isActuallyInstalled(aliases.browser),
|
|
3704
3716
|
'plugin:openclaw-zalo-mod': isActuallyInstalled(aliases.zalo),
|
|
3705
3717
|
'plugin:openclaw-facebook-crawler': isActuallyInstalled(aliases.crawler),
|
|
@@ -3708,31 +3720,34 @@ async function getFeatureFlags(projectDir, agentId = '') {
|
|
|
3708
3720
|
// proof of install — require a real extension dir or an install record instead.
|
|
3709
3721
|
'plugin:openclaw-fb-messenger': extensionDirExists(aliases.fbMessenger)
|
|
3710
3722
|
|| aliases.fbMessenger.some((a) => installedKeys.has(a) || Array.from(installedSpecs).some((spec) => spec.includes(a))),
|
|
3711
|
-
'plugin:memory
|
|
3723
|
+
'plugin:learning-memory': isActuallyInstalled(aliases.learningMemory),
|
|
3712
3724
|
'plugin:zalo-connect': isActuallyInstalled(aliases.zaloConnect),
|
|
3713
3725
|
};
|
|
3714
3726
|
const versions = {
|
|
3715
3727
|
'skill:image-gen': await getInstalledSkillVersion(projectDir, aid, 'infographic-generator', cfg),
|
|
3716
|
-
'skill:learning-memory': await getInstalledSkillVersion(projectDir, aid, 'learning-memory', cfg),
|
|
3717
3728
|
'plugin:openclaw-browser-automation': await getInstalledPluginVersion(projectDir, aliases.browser),
|
|
3718
3729
|
'plugin:openclaw-zalo-mod': await getInstalledPluginVersion(projectDir, aliases.zalo),
|
|
3719
3730
|
'plugin:openclaw-facebook-crawler': await getInstalledPluginVersion(projectDir, aliases.crawler),
|
|
3720
3731
|
'plugin:openclaw-n8n-facebook-poster': await getInstalledPluginVersion(projectDir, aliases.poster),
|
|
3721
3732
|
'plugin:openclaw-fb-messenger': await getInstalledPluginVersion(projectDir, aliases.fbMessenger),
|
|
3722
|
-
'plugin:memory
|
|
3733
|
+
'plugin:learning-memory': await getInstalledPluginVersion(projectDir, aliases.learningMemory),
|
|
3723
3734
|
'plugin:zalo-connect': await getInstalledPluginVersion(projectDir, aliases.zaloConnect),
|
|
3724
3735
|
};
|
|
3725
|
-
// Docker:
|
|
3726
|
-
//
|
|
3736
|
+
// Docker: the container's extensions volume is the SOURCE OF TRUTH for installed
|
|
3737
|
+
// plugin versions — clawhub/plugin installs run inside the container, so a host copy
|
|
3738
|
+
// (bind-mounted .openclaw or a stale installs.json) can lag behind after an update.
|
|
3739
|
+
// When the container reports a version, it OVERRIDES the host value (not just fills
|
|
3740
|
+
// empties) so the card shows the actually-installed version, not a stale one. Native
|
|
3741
|
+
// installs return {} here, so host values are kept.
|
|
3727
3742
|
const containerExtVersions = await getContainerExtensionVersions(projectDir);
|
|
3728
3743
|
if (Object.keys(containerExtVersions).length) {
|
|
3729
|
-
const fillVer = (key, names) => {
|
|
3744
|
+
const fillVer = (key, names) => { for (const n of names) { if (containerExtVersions[n]) { versions[key] = containerExtVersions[n]; break; } } };
|
|
3730
3745
|
fillVer('plugin:openclaw-browser-automation', aliases.browser);
|
|
3731
3746
|
fillVer('plugin:openclaw-zalo-mod', aliases.zalo);
|
|
3732
3747
|
fillVer('plugin:openclaw-facebook-crawler', aliases.crawler);
|
|
3733
3748
|
fillVer('plugin:openclaw-n8n-facebook-poster', aliases.poster);
|
|
3734
3749
|
fillVer('plugin:openclaw-fb-messenger', aliases.fbMessenger);
|
|
3735
|
-
fillVer('plugin:memory
|
|
3750
|
+
fillVer('plugin:learning-memory', aliases.learningMemory);
|
|
3736
3751
|
fillVer('plugin:zalo-connect', aliases.zaloConnect);
|
|
3737
3752
|
}
|
|
3738
3753
|
const zaloBackend = fresh.channels?.['zalo-connect']?.enabled ? 'zalo-connect' : '';
|
|
@@ -371,6 +371,12 @@
|
|
|
371
371
|
|
|
372
372
|
const allow = ['memory-core'];
|
|
373
373
|
|
|
374
|
+
// Always-on memory: the learning-memory context engine injects the curated
|
|
375
|
+
// MEMORY.md + USER.md into every turn (including group sessions, which the default
|
|
376
|
+
// recall excludes). Enabled for every bot; the plugin is installed at creation time.
|
|
377
|
+
entries['learning-memory'] = { enabled: true };
|
|
378
|
+
allow.push('learning-memory');
|
|
379
|
+
|
|
374
380
|
// Zalo Personal → zalo-connect channel plugin (pinned install happens in the
|
|
375
381
|
// entrypoint / creation flow). openclaw-zalo-mod stays a separate opt-in plugin.
|
|
376
382
|
if (isZaloPersonal(channelKey)) {
|
|
@@ -395,6 +401,8 @@
|
|
|
395
401
|
|
|
396
402
|
const plugins = { entries };
|
|
397
403
|
plugins.allow = allow;
|
|
404
|
+
// Select learning-memory as the active context engine (always-on memory).
|
|
405
|
+
plugins.slots = { contextEngine: 'learning-memory' };
|
|
398
406
|
if (allow.length) plugins.bundledDiscovery = 'compat';
|
|
399
407
|
|
|
400
408
|
return { plugins };
|
|
@@ -256,6 +256,11 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
256
256
|
const backupConfigScript = `const fs=require('fs'),path=require('path'),p=path.join(process.cwd(),'.openclaw','openclaw.json'),b=p.replace('openclaw.json','.openclaw-config-backup.json');if(fs.existsSync(p)){fs.copyFileSync(p,b);}`;
|
|
257
257
|
|
|
258
258
|
const restoreConfigScript = `const fs=require('fs'),os=require('os'),path=require('path'),p=path.join(process.cwd(),'.openclaw','openclaw.json'),b=p.replace('openclaw.json','.openclaw-config-backup.json');if(fs.existsSync(p)&&fs.existsSync(b)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const bk=JSON.parse(fs.readFileSync(b,'utf8'));const keep=['agents','channels','bindings','commands','models','browser','skills','plugins','tools'];for(const k of keep){if(bk[k]&&!c[k])c[k]=bk[k];}const gp=Number(process.env.OPENCLAW_GATEWAY_PORT||process.env.OPENCLAW_PORT)||c.gateway?.port||bk.gateway?.port||18789;const a=new Set(['http://localhost:'+gp,'http://127.0.0.1:'+gp,'http://0.0.0.0:'+gp]);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add('http://'+entry.address+':'+gp);}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:gp,bind:'custom',customBindHost:'0.0.0.0',mode:c.gateway?.mode||bk.gateway?.mode||'local',controlUi:Object.assign({},c.gateway?.controlUi,{allowedOrigins:Array.from(a).filter(Boolean)})});fs.writeFileSync(p,JSON.stringify(c,null,2));fs.unlinkSync(b);}`;
|
|
259
|
+
// One-shot migration for projects created by an older setup: the learning-memory
|
|
260
|
+
// SKILL and the memory-tencentdb plugin are deprecated (replaced by the learning-memory
|
|
261
|
+
// context-engine plugin). Drop their config entries so they stop loading; the skill
|
|
262
|
+
// folders + tencentdb install are removed by the shell steps that follow.
|
|
263
|
+
const deprecatedCleanupScript = `const fs=require('fs'),path=require('path');const p=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));let ch=false;if(c.plugins&&c.plugins.entries&&c.plugins.entries['memory-tencentdb']){delete c.plugins.entries['memory-tencentdb'];ch=true;}if(c.plugins&&Array.isArray(c.plugins.allow)){const n=c.plugins.allow.filter(x=>x!=='memory-tencentdb'&&x!=='@tencentdb-agent-memory/memory-tencentdb');if(n.length!==c.plugins.allow.length){c.plugins.allow=n;ch=true;}}if(c.skills&&c.skills.entries&&c.skills.entries['learning-memory']){delete c.skills.entries['learning-memory'];ch=true;}if(ch)fs.writeFileSync(p,JSON.stringify(c,null,2));}`;
|
|
259
264
|
const securityCompatScript = `const fs=require('fs'),path=require('path');const scopes=['operator.admin','operator.pairing','operator.approvals'];function uniq(a){return Array.from(new Set([...(Array.isArray(a)?a:[]),...scopes]));}function walk(v){if(!v||typeof v!=='object')return;if(Array.isArray(v)){v.forEach(walk);return;}if(Array.isArray(v.scopes)||Array.isArray(v.approvedScopes)){v.scopes=uniq(v.scopes);v.approvedScopes=uniq(v.approvedScopes);}Object.values(v).forEach(walk);}const home=process.env.OPENCLAW_HOME||path.join(process.cwd(),'.openclaw');const state=process.env.OPENCLAW_STATE_DIR||home;const cfgPath=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(cfgPath)){const c=JSON.parse(fs.readFileSync(cfgPath,'utf8'));const p=c.models&&c.models.providers&&c.models.providers['9router'];if(p){p.request=Object.assign({},p.request,{allowPrivateNetwork:true});}fs.writeFileSync(cfgPath,JSON.stringify(c,null,2));}for(const root of Array.from(new Set([home,state]))){const f=path.join(root,'devices','paired.json');if(fs.existsSync(f)){const d=JSON.parse(fs.readFileSync(f,'utf8'));walk(d);fs.writeFileSync(f,JSON.stringify(d,null,2));}}`;
|
|
260
265
|
|
|
261
266
|
const runtimeParts = runtimeCommandParts.filter(Boolean);
|
|
@@ -323,6 +328,13 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
323
328
|
const zaloConnectSpec = common.ZALO_CONNECT_PLUGIN_SPEC || 'clawhub:openclaw-zalo-connect';
|
|
324
329
|
runtimeParts.push(`ensure_plugin zalo-connect "${zaloConnectSpec}"`);
|
|
325
330
|
}
|
|
331
|
+
// Always-on memory context engine for every bot (see bot-config-gen: plugins.slots
|
|
332
|
+
// .contextEngine = "learning-memory"). ensure_plugin skips if already installed.
|
|
333
|
+
runtimeParts.push('ensure_plugin learning-memory "clawhub:openclaw-learning-memory"');
|
|
334
|
+
// Migrate off the deprecated learning-memory SKILL + memory-tencentdb plugin (older setups).
|
|
335
|
+
runtimeParts.push(`node - <<'NODE'\n${deprecatedCleanupScript}\nNODE`);
|
|
336
|
+
runtimeParts.push('find "$OPENCLAW_HOME" -maxdepth 4 -type d -path "*/skills/learning-memory" -exec rm -rf {} + 2>/dev/null || true');
|
|
337
|
+
runtimeParts.push('if [ -d "$OPENCLAW_HOME/extensions/memory-tencentdb" ] || openclaw plugins list 2>/dev/null | grep -q memory-tencentdb; then openclaw plugins uninstall memory-tencentdb --force 2>/dev/null || true; fi');
|
|
326
338
|
runtimeParts.push('openclaw gateway run');
|
|
327
339
|
const runtimeScript = ['#!/bin/sh', 'set -e', ...runtimeParts].join('\n');
|
|
328
340
|
let browserInstall = '';
|
|
@@ -693,8 +693,8 @@ Ngoài gửi text, tool \`${zaloDeliveryChannel}\` có ~149 action. Gọi bằng
|
|
|
693
693
|
// straight from the workspace path; files must be copied into the shared outbound media
|
|
694
694
|
// store first, then sent via the `message` tool. Injected into every AGENTS.md variant.
|
|
695
695
|
const fileSendRule = isVi
|
|
696
|
-
? `\n\n## 📤 Quy tắc xuất & gửi file (Excel, tài liệu, ảnh...)\nDo sandbox bảo mật của OpenClaw (nhất là kênh Zalo cá nhân), khi cần gửi BẤT KỲ file nào cho user:\n1. Tạo/xuất file trong workspace của bạn (vd: \`${workspacePath}/bao-cao.xlsx\`).\n2. Tạo thư mục outbound (nếu chưa có) RỒI copy — chạy đúng 1 lệnh: \`mkdir -p /home/node/project/.openclaw/media/outbound && cp <đường-dẫn-file> /home/node/project/.openclaw/media/outbound/\`. (Bỏ qua \`mkdir -p\` thì copy sẽ lỗi khi thư mục chưa tồn tại. Dùng \`cp\`, KHÔNG dùng \`copy\`.)\n3. Gửi cho user bằng tool \`message\` (action="send") với đường dẫn file trong \`media/outbound/\`.\n- ⚠️ **Định dạng:** dùng định dạng HIỆN ĐẠI (\`.xlsx\`, \`.pdf\`, \`.png\`, \`.jpg\`). TUYỆT ĐỐI tránh \`.xls\` đời cũ. Lý do: OpenClaw chỉ cho gửi file mà loại media/tài liệu **xác thực được từ nội dung** (buffer-verified); \`.xls\` chỉ ra MIME fallback (\`application/vnd.ms-excel\`) nên bị CHẶN — đây là policy của OpenClaw, KHÔNG phải do Zalo/Telegram chặn, và KHÔNG liên quan group hay DM. Xuất Excel thì luôn xuất \`.xlsx\`.\n- KHÔNG gửi thẳng từ đường dẫn workspace (dễ bị sandbox chặn).\n- Đặt tên file rõ ràng (kèm thời gian/tên nhóm) để phân biệt; áp dụng cho cả Zalo lẫn Telegram.`
|
|
697
|
-
: `\n\n## 📤 File export & sending rule (Excel, documents, images...)\nDue to OpenClaw's security sandbox (especially the Zalo personal channel), to send ANY file to the user:\n1. Create/export the file in your workspace (e.g. \`${workspacePath}/report.xlsx\`).\n2. Create the outbound dir (if missing) THEN copy — run as one command: \`mkdir -p /home/node/project/.openclaw/media/outbound && cp <file-path> /home/node/project/.openclaw/media/outbound/\`. (Skipping \`mkdir -p\` makes the copy fail when the dir doesn't exist yet. Use \`cp\`, not \`copy\`.)\n3. Send it to the user via the \`message\` tool (action="send") using the path inside \`media/outbound/\`.\n- ⚠️ **Format:** use MODERN formats (\`.xlsx\`, \`.pdf\`, \`.png\`, \`.jpg\`). NEVER use legacy \`.xls\`. Reason: OpenClaw only allows sending files whose media/document type is **buffer-verified** (sniffed from content); \`.xls\` only yields a fallback MIME (\`application/vnd.ms-excel\`) and is BLOCKED — this is an OpenClaw policy, NOT a Zalo/Telegram limit, and is unrelated to group vs DM. Always export Excel as \`.xlsx\`.\n- DO NOT send directly from the workspace path (the sandbox may block it).\n- Use a clear filename (with timestamp/group name); applies to both Zalo and Telegram.`;
|
|
696
|
+
? `\n\n## 📤 Quy tắc xuất & gửi file (Excel, tài liệu, ảnh...)\nDo sandbox bảo mật của OpenClaw (nhất là kênh Zalo cá nhân), khi cần gửi BẤT KỲ file nào cho user:\n1. Tạo/xuất file trong workspace của bạn (vd: \`${workspacePath}/bao-cao.xlsx\`).\n2. Tạo thư mục outbound (nếu chưa có) RỒI copy — chạy đúng 1 lệnh: \`mkdir -p /home/node/project/.openclaw/media/outbound && cp <đường-dẫn-file> /home/node/project/.openclaw/media/outbound/\`. (Bỏ qua \`mkdir -p\` thì copy sẽ lỗi khi thư mục chưa tồn tại. Dùng \`cp\`, KHÔNG dùng \`copy\`.)\n3. Gửi cho user bằng tool \`message\` (action="send") với đường dẫn file trong \`media/outbound/\`.\n- ⚠️ **Định dạng:** dùng định dạng HIỆN ĐẠI (\`.xlsx\`, \`.pdf\`, \`.png\`, \`.jpg\`). TUYỆT ĐỐI tránh \`.xls\` đời cũ. Lý do: OpenClaw chỉ cho gửi file mà loại media/tài liệu **xác thực được từ nội dung** (buffer-verified); \`.xls\` chỉ ra MIME fallback (\`application/vnd.ms-excel\`) nên bị CHẶN — đây là policy của OpenClaw, KHÔNG phải do Zalo/Telegram chặn, và KHÔNG liên quan group hay DM. Xuất Excel thì luôn xuất \`.xlsx\`.\n- KHÔNG gửi thẳng từ đường dẫn workspace (dễ bị sandbox chặn).\n- Đặt tên file rõ ràng (kèm thời gian/tên nhóm) để phân biệt; áp dụng cho cả Zalo lẫn Telegram.\n- ⛔ **CHỈ dùng \`message\`(action="send") cho FILE hoặc khi CHỦ ĐỘNG nhắn sang thread KHÁC.** Để TRẢ LỜI hội thoại hiện tại (chữ), chỉ cần VIẾT nội dung ra — hệ thống tự gửi ĐÚNG 1 LẦN (kèm mention/quote). TUYỆT ĐỐI KHÔNG gọi \`message\`/\`send\` để trả lời thread hiện tại (kể cả sau khi đã dùng tool như phân tích ảnh) → sẽ bị **GỬI ĐÔI**. Và KHÔNG viết câu trạng thái nội bộ kiểu "Đã xử lý xong…", "Không cần thêm gì nữa", "NO_REPLY" — đó là lộ suy nghĩ; chỉ trả lời đúng nội dung, một lần rồi dừng.`
|
|
697
|
+
: `\n\n## 📤 File export & sending rule (Excel, documents, images...)\nDue to OpenClaw's security sandbox (especially the Zalo personal channel), to send ANY file to the user:\n1. Create/export the file in your workspace (e.g. \`${workspacePath}/report.xlsx\`).\n2. Create the outbound dir (if missing) THEN copy — run as one command: \`mkdir -p /home/node/project/.openclaw/media/outbound && cp <file-path> /home/node/project/.openclaw/media/outbound/\`. (Skipping \`mkdir -p\` makes the copy fail when the dir doesn't exist yet. Use \`cp\`, not \`copy\`.)\n3. Send it to the user via the \`message\` tool (action="send") using the path inside \`media/outbound/\`.\n- ⚠️ **Format:** use MODERN formats (\`.xlsx\`, \`.pdf\`, \`.png\`, \`.jpg\`). NEVER use legacy \`.xls\`. Reason: OpenClaw only allows sending files whose media/document type is **buffer-verified** (sniffed from content); \`.xls\` only yields a fallback MIME (\`application/vnd.ms-excel\`) and is BLOCKED — this is an OpenClaw policy, NOT a Zalo/Telegram limit, and is unrelated to group vs DM. Always export Excel as \`.xlsx\`.\n- DO NOT send directly from the workspace path (the sandbox may block it).\n- Use a clear filename (with timestamp/group name); applies to both Zalo and Telegram.\n- ⛔ **Use \`message\`(action="send") ONLY for files or when PROACTIVELY messaging a DIFFERENT thread.** To REPLY to the current conversation (text), just WRITE your response — the system delivers it exactly ONCE (with mention/quote). NEVER call \`message\`/\`send\` to reply to the current thread (even after using a tool like image analysis) → it causes a **DOUBLE message**. And never emit internal status lines like "Done…", "Nothing more needed", or "NO_REPLY" — that leaks your reasoning; just reply with the content, once, then stop.`;
|
|
698
698
|
const securityRules = includeSecurity ? buildSecurityRules(isVi) : '';
|
|
699
699
|
|
|
700
700
|
// Doc structure mirrors OpenClaw's default AGENTS.md (Session Startup, Memory, Red Lines,
|
package/dist/web/app.js
CHANGED
|
@@ -1061,11 +1061,10 @@ function botSkillsPanel() {
|
|
|
1061
1061
|
{ id: 'cron', title: 'Cron', desc: 'Native scheduler (SQLite) — cron guide in TOOLS.md' },
|
|
1062
1062
|
{ id: 'image-gen', title: 'Tạo ảnh Infographic', desc: 'Tạo ảnh infographic, poster tự động qua 9Router' },
|
|
1063
1063
|
{ id: 'web-search', title: 'Web Search', desc: 'Tìm kiếm web thời gian thực (DuckDuckGo)' },
|
|
1064
|
-
{ id: 'learning-memory', title: 'Siêu Trí Nhớ Dài Hạn (learning-memory)', desc: 'Tự động ghi nhớ bài học vào MEMORY.md, tự đóng gói và tiến hóa kỹ năng mới vào skills/' },
|
|
1065
1064
|
];
|
|
1066
1065
|
const plugins = [
|
|
1067
1066
|
{ id: 'zalo-connect', title: 'OpenClaw Zalo Connect', desc: t('Kênh Zalo cá nhân (zca-js) — BẮT BUỘC cho bot Zalo. Tự cài khi tạo bot Zalo đầu tiên; bấm Cập nhật để lên bản mới nhất.', 'Personal Zalo channel (zca-js) — REQUIRED for Zalo bots. Auto-installed with your first Zalo bot; click Update for the latest version.'), channels: ['zalo-personal'] },
|
|
1068
|
-
{ id: 'memory
|
|
1067
|
+
{ id: 'learning-memory', title: 'Siêu Trí Nhớ Dài Hạn (learning-memory)', desc: t('Bộ nhớ always-on: nạp MEMORY.md + USER.md đã chắt lọc vào MỌI lượt — kể cả trong nhóm — nên bot không quên ngữ cảnh/quy tắc. Tự cài khi tạo bot.', 'Always-on memory: injects a curated MEMORY.md + USER.md into every turn — including group chats — so the bot stops forgetting context and rules. Auto-installed on bot creation.') },
|
|
1069
1068
|
{ id: 'openclaw-browser-automation', title: 'openclaw-browser-automation', desc: 'Smart Search + Browser (headless & Chrome thật)' },
|
|
1070
1069
|
{ id: 'openclaw-zalo-mod', title: 'openclaw-zalo-mod', desc: 'Zalo group helpers', channels: ['zalo-personal'], openWebPort: 18790, openWebPath: '/dashboard' },
|
|
1071
1070
|
{ id: 'openclaw-fb-messenger', title: 'openclaw-fb-messenger', desc: t('Kênh Facebook Messenger — webhook + Graph API (bắt buộc cho bot Messenger)', 'Facebook Messenger channel — webhook + Graph API (required for Messenger bots)'), channels: ['fb-messenger'] },
|
|
@@ -1085,7 +1084,7 @@ function botSkillsPanel() {
|
|
|
1085
1084
|
const key = `${group}:${item.id}`;
|
|
1086
1085
|
const loading = !!state.featureLoading[key];
|
|
1087
1086
|
const locked = !!state.featureLocked?.[key];
|
|
1088
|
-
const requiresInstall = group === 'plugin' || (group === 'skill' &&
|
|
1087
|
+
const requiresInstall = group === 'plugin' || (group === 'skill' && item.id === 'image-gen');
|
|
1089
1088
|
const isInstalled = !requiresInstall || !!state.featureInstalled?.[key] || locked;
|
|
1090
1089
|
|
|
1091
1090
|
const version = requiresInstall && isInstalled ? (state.featureVersions?.[key] || '') : '';
|