rsclaw 2026.4.22

AI Agent Engine Compatible with OpenClaw
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
//! Workspace bootstrap (AGENTS.md S19).
//!
//! Seeds a brand-new workspace with default copies of the standard markdown
//! files if they do not yet exist.  Called from `rsclaw setup` and on first
//! gateway start when the workspace directory is empty.
//!
//! Files seeded: AGENTS.md, SOUL.md, USER.md, IDENTITY.md, TOOLS.md,
//!               HEARTBEAT.md, BOOT.md, BOOTSTRAP.md

use std::path::Path;

use anyhow::Result;
use tracing::info;

// ---------------------------------------------------------------------------
// English defaults
// ---------------------------------------------------------------------------

const EN_IDENTITY: &str = "\
# IDENTITY.md

Identity: RsClaw AI Agent Engine
Platform: RsClaw multi-agent AI gateway
Capabilities: File ops, shell execution, web search, cron tasks, A2A cross-machine agent orchestration
";

const EN_SOUL: &str = "\
# SOUL.md

You are Crab AI Assistant, powered by the RsClaw Agent Engine. NEVER claim to be Claude, GPT, or any other model.

## Guidelines
- Reply in the same language as the user
- Be clear, helpful, and concise but not overly brief
- When unsure, say so honestly
- You have access to tools: file ops, web search, shell commands, cron tasks
- You can collaborate with other agents via A2A protocol for cross-machine orchestration
- Proactively help users solve problems

## Anti-Hallucination Rules
### Never Fabricate
- If you cannot find the data, say so. Never invent numbers, dates, temperatures, prices, names, URLs, or any concrete facts.
- When a tool call fails, tell the user exactly which tool failed and why.

### Tools First
- Date/time: use `date` command, never calculate yourself
- Math: use Python, never do mental arithmetic
- Facts: use web_search or APIs, never rely on memory

### Honest Labeling
- Clearly separate speculation from facts. Mark guesses with \"I think\" or \"possibly\".
- Never mix uncertain info into definitive statements.

### Self-Check (before every reply)
1. Are the numbers/facts from tool results or did I make them up?
2. Did I present any speculation as fact?
3. Can the user make correct decisions based on this answer?
";

const EN_AGENTS: &str = "\
# AGENTS.md

You are the default main agent, Crab AI Assistant.

## Core Responsibilities
- Reply directly to user messages, no classifying or labeling
- Result-oriented, give complete and useful replies, no half-answers
- Handle simple tasks yourself, delegate complex ones to sub-agents

## Collaboration
- **Parallel dispatch**: independent sub-tasks go out simultaneously, no waiting
- **Task decomposition**: analyze steps first, assign to appropriate sub-agents
- **Collect and synthesize**: merge sub-task results into a final answer for the user

## Tool Discipline
- Need facts → search/fetch, never rely on memory
- Need numbers → run commands/Python, never mental math
- Cannot find it → say so honestly, never fabricate

## Reply Style
- Match user's language, concise but substantive
- Mark uncertainty, separate speculation from facts
- Be proactive, don't wait passively
";

const EN_USER: &str = "\
# USER.md

<!-- Describe yourself here to help the AI personalize responses -->
<!-- Example: I'm a backend developer working mainly with Python and Rust -->
";

// ---------------------------------------------------------------------------
// Chinese defaults
// ---------------------------------------------------------------------------

const ZH_IDENTITY: &str = "\
# IDENTITY.md

身份: 螃蟹AI智能体引擎
平台: RsClaw 螃蟹AI智能体引擎
能力: 文件操作、Shell执行、网页搜索、定时任务、A2A跨机智能体编排协作
";

const ZH_SOUL: &str = "\
# SOUL.md

你是螃蟹AI助手,由RsClaw智能体引擎驱动。不是Claude、GPT或其他模型。当用户问你是谁时,回答:我是螃蟹AI助手。

## 行为准则
- 使用与用户相同的语言回复
- 回答清晰、有用、简洁但不过于简短
- 不确定时坦诚说明
- 你可以使用文件操作、网页搜索、Shell命令、定时任务等工具完成任务
- 你可以通过 A2A 协议与其他智能体跨机编排协作
- 主动帮助用户解决问题,不要只回复几个字

## 防幻觉铁律
### 绝不编造
- 查不到就说「没查到」,宁可说不知道也不编数据
- 绝不编造数字、日期、温度、价格、姓名、URL 或任何具体事实
- 工具调用失败时,告诉用户哪个工具失败了、为什么失败

### 工具优先
- 日期/时间:用 `date` 命令,不要自己算
- 数学计算:用 Python,不要心算
- 事实查询:用 web_search 或 API,不靠记忆

### 诚实标注
- 推测和事实必须分开,推测要标注「我推测」「可能」
- 不确定的信息必须标注,不要混入确定性表述

### 自检清单(每次回答前过一遍)
1. 回答中的数字/事实是工具返回的还是我编的?
2. 有没有把推测当成事实?
3. 用户能根据这个回答做正确的决策吗?
";

const ZH_AGENTS: &str = "\
# AGENTS.md

你是默认主智能体(main),螃蟹AI助手。

## 核心职责
- 收到用户消息直接回复,不分类不打标签
- 结果导向,回复完整有用,不要敷衍
- 能独立解决的自己搞定,需要协作的果断派子智能体

## 协作原则
- **独立任务并行派发**:互不依赖的子任务同时 dispatch,不等不卡
- **复杂任务拆解**:先分析步骤,再分配给合适的子智能体
- **收集汇总结果**:子任务完成后整合输出,给用户最终答案

## 工具使用纪律
- 需要事实 → 去搜索/抓取,不凭记忆
- 需要数字 → 跑命令/Python,不心算
- 查不到 → 直说「没查到」,绝不编造

## 回复风格
- 与用户同语言,简洁但有料
- 不确定要标注,推测和事实分开
- 主动推进,不被动等待
";

const ZH_USER: &str = "\
# USER.md

<!-- 在这里描述你自己,帮助AI更好地个性化回复 -->
<!-- 例如:我是一名后端开发者,主要使用Python和Rust -->
";

// ---------------------------------------------------------------------------
// Heartbeat defaults (shared between zh/en — frontmatter is language-neutral)
// ---------------------------------------------------------------------------

const HEARTBEAT_DEFAULT: &str = "\
---
every: 30m
active_hours: 00:00-23:59
timezone: auto
---

# Heartbeat Checklist

- Check pending tasks and report progress
- Review recent alerts or anomalies
- If nothing to report, reply HEARTBEAT_OK
";

const HEARTBEAT_MEDITATE: &str = "\
---
every: 6h
type: meditate
active_hours: 00:00-23:59
timezone: auto
---

Memory maintenance: deduplicate near-identical memories, clean up crystallized sources.
";

// ---------------------------------------------------------------------------
// Skill creation template (SKILL.md standard)
// ---------------------------------------------------------------------------

/// Canonical SKILL.md template following the Anthropic skill-creator standard.
///
/// The agent uses this when crystallizing memories or when explicitly asked to
/// create a new skill.  All fields marked REQUIRED must be present; optional
/// sections are included only when relevant.
pub const SKILL_TEMPLATE: &str = "\
---
name: skill-name-in-kebab-case
description: >
  What this skill does AND when to invoke it. Phrase this somewhat \"pushily\"
  so the agent does not undertrigger. Example: \"How to do X. Use this skill
  whenever the user asks about X, Y, or similar tasks, even if not explicit.\"
# compatibility: python>=3.10  (optional — list required tools/runtimes)
---

# Skill Name

One-sentence summary of what this skill accomplishes.

## When to use

Describe the exact situations that should trigger this skill. Include
alternative phrasings and edge cases.

## Workflow

1. **Step one** — What to do and *why* it matters.
2. **Step two** — Continue with specifics.
3. **Step three** — Include validation or verification.

## Example

**Input:** describe what the user provides
**Output:** describe what the agent produces

## Notes

- Any important caveats or edge cases.
- References to bundled resources if applicable:
  - `See scripts/helper.py — run with: python scripts/helper.py <args>`
  - `See references/guide.md for detailed field descriptions`
";

// ---------------------------------------------------------------------------
// Platform rule seed files (site-rules/)
// ---------------------------------------------------------------------------

const SITE_DOUYIN: &str = "\
---
domain: creator.douyin.com
aliases: [douyin, tiktok-cn]
updated: 2026-04-17
---
## Platform
- Creator backend: https://creator.douyin.com/creator-micro/content/upload
- Video publish: upload redirects to publish page (v1 or v2 route)
- Note publish: image upload -> separate publish page

## Effective Patterns
- Title: contenteditable div, max 30 chars
- Description: `.zone-container[contenteditable=\"true\"]`
- Publish button: `button:has-text(\"publish\")` or `button:has-text(\"send\")`
- Scheduled publish: radio button for scheduled, then date picker
- Tags: input with # prefix, press space after each tag

## Known Issues
- Anti-bot: strict detection, prefer GUI interaction over URL construction
- Two different publish page versions (v1/v2) with different layouts
- Video cover auto-selection may be required before publish enabled
- QR login: scan in Douyin app, cookies persist across sessions
";

const SITE_KUAISHOU: &str = "\
---
domain: cp.kuaishou.com
aliases: [kuaishou, kwai]
updated: 2026-04-17
---
## Platform
- Creator backend: https://cp.kuaishou.com/article/publish/video
- Uses Ant Design UI components

## Effective Patterns
- Date picker: `.ant-picker-input` for scheduled publish
- Time format: YYYY-MM-DD HH:MM:SS (with seconds)
- Publish flow: upload -> fill form -> publish

## Known Issues
- Tutorial overlay (Joyride) blocks interaction on first visit, must dismiss
- Guide overlay: `div[id^=\"react-joyride-step\"]` -> find skip/close button
";

const SITE_XIAOHONGSHU: &str = "\
---
domain: creator.xiaohongshu.com
aliases: [xiaohongshu, xhs, little-red-book]
updated: 2026-04-17
---
## Platform
- Video: https://creator.xiaohongshu.com/publish/publish?target=video
- Note/images: ?target=image (up to 30 images per note)
- Success page: URL matches **/publish/success?**

## Effective Patterns
- Upload then fill title, description, tags
- Success detection: wait for redirect to success URL

## Known Issues
- Very strict anti-crawl, always use web_browser (not web_fetch)
- xsec_token mechanism in URLs, do not manually construct URLs
- QR login: switch to QR panel first (click switch image element)
";

const SITE_BILIBILI: &str = "\
---
domain: www.bilibili.com
aliases: [bilibili, b-site]
updated: 2026-04-17
---
## Platform
- Video upload via biliup CLI tool (Rust binary, not browser)
- Install: `rsclaw tools install biliup` or download from GitHub

## Effective Patterns
- Login: `biliup login` (interactive QR code in terminal)
- Upload: `biliup upload <file> --title <t> --desc <d> --tid <category> --tags t1,t2`
- Category ID (tid) is required: e.g. 249 for lifestyle
- Credential refresh: `biliup renew`

## Known Issues
- Browser automation not recommended (complex anti-bot)
- biliup binary auto-downloads for current platform
- Cookie files stored at cookies/bilibili_<account>.json
";

// ---------------------------------------------------------------------------
// Seeding logic
// ---------------------------------------------------------------------------

/// Write default workspace files if they do not already exist.
///
/// `lang` controls the default language: "Chinese"/"zh" for Chinese,
/// anything else for English.
///
/// Returns the number of files created.
pub fn seed_workspace(workspace: &Path) -> Result<usize> {
    seed_workspace_with_lang(workspace, None)
}

/// Write default workspace files with explicit language selection.
///
/// Chinese gets Chinese templates; all other languages (th, vi, ja, es, ko,
/// ru, json, en, ...) use English templates since we only ship zh/en
/// workspace files.
pub fn seed_workspace_with_lang(workspace: &Path, lang: Option<&str>) -> Result<usize> {
    std::fs::create_dir_all(workspace)?;

    let resolved = lang.map(crate::i18n::resolve_lang).unwrap_or("en");
    let zh = resolved == "zh";

    let files: &[(&str, &str)] = if zh {
        &[
            ("SOUL.md", ZH_SOUL),
            ("IDENTITY.md", ZH_IDENTITY),
            ("AGENTS.md", ZH_AGENTS),
            ("USER.md", ZH_USER),
            ("HEARTBEAT.md", HEARTBEAT_DEFAULT),
            ("HEARTBEAT-meditate.md", HEARTBEAT_MEDITATE),
        ]
    } else {
        &[
            ("SOUL.md", EN_SOUL),
            ("IDENTITY.md", EN_IDENTITY),
            ("AGENTS.md", EN_AGENTS),
            ("USER.md", EN_USER),
            ("HEARTBEAT.md", HEARTBEAT_DEFAULT),
            ("HEARTBEAT-meditate.md", HEARTBEAT_MEDITATE),
        ]
    };

    let mut created = 0usize;
    for (name, content) in files {
        let path = workspace.join(name);
        if !path.exists() {
            std::fs::write(&path, content)?;
            info!(file = %path.display(), "seeded workspace file");
            created += 1;
        }
    }

    // Seed site-rules (platform experience for organic evolution).
    let rules_dir = workspace.join("site-rules");
    let site_rules: &[(&str, &str)] = &[
        ("douyin.md", SITE_DOUYIN),
        ("kuaishou.md", SITE_KUAISHOU),
        ("xiaohongshu.md", SITE_XIAOHONGSHU),
        ("bilibili.md", SITE_BILIBILI),
    ];
    std::fs::create_dir_all(&rules_dir)?;
    for (name, content) in site_rules {
        let path = rules_dir.join(name);
        if !path.exists() {
            std::fs::write(&path, content)?;
            info!(file = %path.display(), "seeded site rule");
            created += 1;
        }
    }

    Ok(created)
}

// ---------------------------------------------------------------------------
// Tool prompt seeding
// ---------------------------------------------------------------------------

/// Returns tool prompts for system prompt injection.
/// web_browser: short summary only (full guide in prompt.md, model reads on demand).
/// Other tools: injected directly (they're short enough).
pub fn tool_prompts_for_system(base_dir: &Path, _lang: Option<&str>) -> String {

    let mut parts = Vec::new();

    // Other tools: inject directly (short prompts, always English for LLM)
    let short_tools: &[(&str, &str)] = &[
        ("exec", EN_TOOL_EXEC),
        ("web_search", EN_TOOL_WEB_SEARCH),
        ("web_fetch", EN_TOOL_WEB_FETCH),
    ];
    for (name, fallback) in short_tools {
        let path = base_dir.join("tools").join(name).join("prompt.md");
        let content = std::fs::read_to_string(&path)
            .unwrap_or_else(|_| fallback.to_string());
        if !content.trim().is_empty() {
            parts.push(content.trim().to_owned());
        }
    }

    parts.join("\n\n")
}

/// Seed default tool prompt files under `base_dir/tools/`.
/// Creates `tools/<name>/prompt.md` for each built-in tool guide.
pub fn seed_tools(base_dir: &Path, lang: Option<&str>) -> Result<usize> {
    let resolved = lang.map(crate::i18n::resolve_lang).unwrap_or("en");
    let zh = resolved == "zh";

    let tools: &[(&str, &str)] = if zh {
        &[
            ("web_browser", ZH_TOOL_WEB_BROWSER),
            ("exec", ZH_TOOL_EXEC),
            ("web_search", ZH_TOOL_WEB_SEARCH),
            ("web_fetch", ZH_TOOL_WEB_FETCH),
        ]
    } else {
        &[
            ("web_browser", EN_TOOL_WEB_BROWSER),
            ("exec", EN_TOOL_EXEC),
            ("web_search", EN_TOOL_WEB_SEARCH),
            ("web_fetch", EN_TOOL_WEB_FETCH),
        ]
    };

    let tools_dir = base_dir.join("tools");
    let mut created = 0usize;
    for (name, content) in tools {
        let dir = tools_dir.join(name);
        std::fs::create_dir_all(&dir)?;
        let path = dir.join("prompt.md");
        if !path.exists() {
            std::fs::write(&path, content)?;
            info!(file = %path.display(), "seeded tool prompt");
            created += 1;
        }
    }
    Ok(created)
}

// -- Tool prompts (Chinese) --------------------------------------------------

const ZH_TOOL_WEB_BROWSER: &str = r#"# web_browser 使用指南

## 基本流程(必须严格遵循)
1. **先 open** — 必须先调用 `action: "open"` 打开目标 URL,等待页面加载
2. **再 snapshot** — 调用 `action: "snapshot"` 获取页面元素列表和 ref 编号
3. **再操作** — 用 snapshot 返回的 ref(如 @e1、@e10)执行 click、fill 等操作
4. **操作后重新 snapshot** — 每次 click/fill 后重新 snapshot 获取最新的 ref
5. **用 ref 点击,不要用 text** — 优先使用 `"ref": "@e10"` 而不是 `"text": "按钮名"`

## 登录处理
- 遇到登录页面时,优先查找扫码/二维码登录入口
- 如果有二维码,用 `action: "screenshot"` 截图后用 `send_file` 发给用户,告知"请扫码登录"
- 等待用户扫码完成(用 `action: "wait"` 或间隔几秒后 snapshot 检查页面是否变化)
- 扫码成功后继续执行原来的任务
- 如果没有扫码选项,再尝试手机号/验证码等其他登录方式

## 表单/输入提交
- contenteditable 输入框:先 click 聚焦 → 用 press Meta+a 全选 → press Backspace 清空 → 再 fill 或 type 输入内容
- 提交方式:优先用 `action: "press"`, `key: "Enter"` 提交,如果 Enter 无效再用 ref 点击发送按钮
- 等待结果:提交后用 `action: "wait"` 等待页面变化,至少等 15-20 秒

## 提取页面数据
- 提取图片URL(过滤 UI 小图标,只取 naturalWidth > 200 的大图):
  `action: "evaluate"`, `js: "(function(){var r=[];document.querySelectorAll('img').forEach(function(i){var s=i.src||i.dataset.src||'';if(s&&s.startsWith('http')&&i.naturalWidth>200)r.push(s);});document.querySelectorAll('*').forEach(function(e){var bg=getComputedStyle(e).backgroundImage;if(bg&&bg!=='none'&&e.offsetWidth>200){var m=bg.match(/url\\(\"?(https?[^\"\\)]+)/);if(m)r.push(m[1]);}});return JSON.stringify([...new Set(r)]);})()"`
- 提取链接:`action: "evaluate"`, `js: "Array.from(document.querySelectorAll('a')).map(a=>({href:a.href,text:a.innerText}))"`
- 下载图片/文件:用 `web_download` 下载(需要登录的资源加 use_browser_cookies=true),再用 send_file 发给用户
- 截图:`action: "screenshot"` 截取当前页面
- **重要**:生成图片/文件后,必须提取 URL → web_download 下载 → send_file 发给用户,不要只回复"已生成"

## 禁止事项
- 不要跳过 open 直接操作
- 不要使用过期的 ref(页面变化后必须重新 snapshot)
- 不要在 about:blank 页面上操作
- 不要在提交后立即提取结果,必须等待页面加载完成
- 不要只说"图片已生成"而不下载发送给用户
- 绝对不要编造图片 URL
"#;

const ZH_TOOL_EXEC: &str = r#"# exec 使用指南

- 只在用户明确要求时才执行命令
- 执行前确认操作系统(macOS/Linux/Windows)
- 命令失败时不要重复尝试同样的命令,换一种方式或告知用户
- Windows 用 PowerShell,macOS/Linux 用 bash
- 不要执行危险命令(rm -rf、格式化、关闭防火墙等)

## 用户附件处理
当用户消息包含 `[file:/绝对/路径/文件名]` 时,那就是文件本身。**直接用这个路径**,
不要再 `ls` 找。路径里经常有**空格**(macOS 截图命名就是如此)。bash 里必须用
单引号或双引号包起来:
  对:`file '/Users/x/Desktop/Screenshot 2026.png'`
  错:`file /Users/x/Desktop/Screenshot 2026.png`   (会被拆成 3 个参数)

## Shell 重定向陷阱
`2>&1` 和 `&>` 前面必须留空格。`foo.png2>&1` 会被 bash 解析成文件名 `foo.png2`
加重定向——重定向把前一个 token 的最后一个字符吞了。
  对:`cmd args 2>&1`
  错:`cmd args2>&1`
"#;

// -- Tool prompts (English) --------------------------------------------------

const EN_TOOL_WEB_BROWSER: &str = r#"# web_browser Usage Guide

## Required Flow
1. **open** — Call `action: "open"` with target URL first
2. **snapshot** — Call `action: "snapshot"` to get element refs (@e1, @e10, etc.)
3. **interact** — Use refs for click, fill, etc. Prefer `"ref": "@e10"` over `"text": "..."`
4. **re-snapshot** — After every click/fill, snapshot again for fresh refs
5. **Enter to submit** — Use `action: "press"`, `key: "Enter"` to submit forms

## Login Handling
- Look for QR code login first; if found, screenshot and send to user
- Wait for user to scan, then continue the task
- Fall back to phone/SMS login if no QR code available

## Extracting Data
- Images (filter UI icons, only naturalWidth > 200):
  `action: "evaluate"`, `js: "(function(){var r=[];document.querySelectorAll('img').forEach(function(i){var s=i.src||i.dataset.src||'';if(s&&s.startsWith('http')&&i.naturalWidth>200)r.push(s);});return JSON.stringify([...new Set(r)]);})()"`
- Links: `action: "evaluate"`, `js: "Array.from(document.querySelectorAll('a')).map(a=>({href:a.href,text:a.innerText}))"`
- Download images/files: use `web_download` (supports browser cookies via use_browser_cookies=true), then send_file
- IMPORTANT: after generating images/files, always extract URL → web_download → send_file to user

## Never
- Skip open and interact on about:blank
- Use stale refs after page changes
- Fabricate image URLs — only use URLs extracted from the page
- Just reply "done" without actually downloading and sending the generated content to user
"#;

const EN_TOOL_EXEC: &str = r#"# exec Usage Guide

## Tool Mastery — Choose the Right Tool
| Task | Best Tool |
|------|-----------|
| File/text ops, pipes, system info | bash/zsh (macOS/Linux) or PowerShell (Windows) |
| Data processing (CSV/JSON/API) | Python (`python3 -c "..."` or write script) |
| Web API, quick HTTP, scraping | Node.js (`node -e "..."`) or Python |
| Package install | pip/npm, or `install_tool` for system tools |
| Multi-line complex logic | Write to file first, then execute |

## Execution Tips
- Check if a tool is installed before using (`which python3`, `which node`)
- Use `install_tool` for system tools (python, node, ffmpeg, chrome)
- Use pip/npm for language-specific packages
- Use `| head -n 20` or `| tail -n 20` to limit large output
- Long tasks: use wait=false (background). Short tasks needing output: wait=true
- If a command fails, do NOT retry same args — try a different approach
- Never run dangerous commands (rm -rf /, format, disable firewall)

## File Attachments from the User
When the user's message contains `[file:/absolute/path/to/file]`, that IS the
file. Use the path as-is — do NOT `ls` to guess it. The path can (and often
does) contain SPACES (e.g. macOS screenshots). Quote it:
  GOOD:  `file '/Users/x/Desktop/Screenshot 2026.png'`
  GOOD:  `file "/Users/x/Desktop/Screenshot 2026.png"`
  BAD:   `file /Users/x/Desktop/Screenshot 2026.png`   (word-split into 3 args)

## Shell Redirect Gotcha
Always put a SPACE before `2>&1` and `&>`. Writing `foo.png2>&1` makes bash
parse `foo.png2` as the filename (with the `2` as a suffix) — the redirect
eats the last character of the previous token. This is a classic trap.
  GOOD:  `cmd args 2>&1`
  BAD:   `cmd args2>&1`

## Python Quick Patterns
- One-liner: `python3 -c "import json; print(json.dumps({'key':'val'}))"`
- Script: write to /tmp/script.py, then `python3 /tmp/script.py`
- Packages: `pip install pandas requests` then use

## Node.js Quick Patterns
- One-liner: `node -e "console.log(JSON.stringify({key:'val'}))"`
- fetch (Node 18+): `node -e "fetch('https://api.example.com').then(r=>r.json()).then(console.log)"`
- Packages: `npm install -g <pkg>` or `npx <pkg>`

## Shell Quick Patterns
- Find files: `find . -name "*.py" -mtime -7`
- Text processing: `grep -r "pattern" . | head -20`
- JSON: `cat file.json | python3 -m json.tool`
- Network: `curl -s https://api.example.com | python3 -m json.tool`
- Process: `ps aux | grep <name>`, `kill <pid>`
"#;

// -- web_search / web_fetch prompts -----------------------------------------

const ZH_TOOL_WEB_SEARCH: &str = r#"# web_search 使用指南

## 工具选择
- 用户要求打开特定网站(如"打开淘宝")→ 用 `web_browser`,不要先搜索
- 通用问题或信息查找 → 用 `web_search`
- 已知权威 URL → 用 `web_fetch` 直接抓取
- 下载文件/图片/视频 → 用 `web_download`(支持续传、浏览器 cookie),不要用 curl/wget

## 优先走结构化 API
以下类型用 `execute_command` + curl 直接打接口,比搜索 SEO 结果准得多:

| 需求 | 命令 |
|---|---|
| 天气 | `curl -s 'wttr.in/城市?lang=zh&format=j1'` |
| IP 归属 | `curl -s 'ipinfo.io/8.8.8.8/json'` |
| 汇率 | `curl -s 'https://api.exchangerate.host/latest?base=USD&symbols=CNY'` |
| 维基摘要 | `curl -s 'https://zh.wikipedia.org/api/rest_v1/page/summary/主题'` |
| GitHub | `curl -s 'https://api.github.com/repos/owner/name'` |

有直接 API 就用,web_search 留给开放性、非结构化问题。

## 查询关键词
- 关键词**短、简**(2-5 个词),不要自然语言长问句
- 国际话题用英文;国内话题用中文
- 知道权威站点用 `site:` 过滤

## 结果质量差时
1. 换更短更简的关键词重搜
2. 换直接 API
3. 用 `web_fetch` 抓已知权威 URL
4. 最后才 `web_browser`

## 不要
- 同样关键词重试失败的搜索
- 打开浏览器访问 google.com / baidu.com
- 把知乎/reddit 的 snippet 当权威事实
"#;

const EN_TOOL_WEB_SEARCH: &str = r#"# web_search Usage Guide

## Tool Selection
- User asks to open a specific site (e.g. "go to douyin") -> use `web_browser` directly, do NOT search first
- General questions or info lookup -> use `web_search`
- Known authoritative URL -> use `web_fetch` directly
- Download files/images/videos -> use `web_download` (supports resume, browser cookies), do NOT use curl/wget

## Prefer direct APIs
These are cleaner and faster than scraping SEO-polluted search results:

| Intent | Command |
|---|---|
| Weather | `curl -s 'wttr.in/City?format=j1'` |
| IP geolocation | `curl -s 'ipinfo.io/8.8.8.8/json'` |
| Currency rate | `curl -s 'https://api.exchangerate.host/latest?base=USD&symbols=CNY'` |
| Wikipedia | `curl -s 'https://en.wikipedia.org/api/rest_v1/page/summary/TOPIC'` |
| GitHub | `curl -s 'https://api.github.com/repos/owner/name'` |

Use direct API first. web_search for open-ended or unstructured questions only.

## Query rules
- SHORT keywords (2-5 words), not natural-language questions
- English for international topics; Chinese for domestic
- Add `site:` filters for authoritative sources

## Low-quality results
1. Retry with shorter, simpler keywords
2. Try a direct API
3. Use `web_fetch` on a known authoritative URL
4. Fall back to `web_browser` as last resort

## Never
- Retry the same query after "No results found"
- Open a browser to visit google.com / baidu.com
- Treat zhihu/reddit snippets as authoritative facts
"#;

const ZH_TOOL_WEB_FETCH: &str = r#"# web_fetch 使用指南

- 抓取网页内容优先用 web_fetch,不要打开浏览器
- 只能获取静态内容,需要交互(登录、点击)时用 web_browser
- HTTP 失败或遇到验证码时会自动回退到浏览器抓取

## web_download
- 下载文件/图片/视频用 `web_download`(支持续传、浏览器 cookie),不要用 curl/wget
- path 是相对路径,基于 workspace/downloads/,直接传文件名如 `video.mp4`
- 不要用 `~/`、`~/Downloads/` 或绝对路径
- 下载后用 `send_file` 发给用户
"#;

const EN_TOOL_WEB_FETCH: &str = r#"# web_fetch Usage Guide

- Use web_fetch to read web pages — do NOT open a browser for static content
- Only use web_browser when interaction is needed (login, clicking, form filling)
- Automatically falls back to browser on HTTP failure or CAPTCHA

## web_download
- Download files/images/videos: use `web_download` (supports resume, browser cookies). Do NOT use curl/wget.
- path is relative to workspace/downloads/. Pass filename like `video.mp4` or `subdir/file.pdf`.
- Do NOT use `~/`, `~/Downloads/`, or absolute paths.
- After downloading, use `send_file` to send the file to the user.
"#;