@buda-ai/connector 0.1.0 → 0.1.2

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.
Files changed (56) hide show
  1. package/README.md +22 -1
  2. package/agent-templates/scripts/skill-scanner.sh +125 -0
  3. package/agent-templates/shared/AGENTS.md +220 -0
  4. package/agent-templates/shared/BOOTSTRAP.md +15 -0
  5. package/agent-templates/shared/HEARTBEAT.md +5 -0
  6. package/agent-templates/shared/IDENTITY.md +29 -0
  7. package/agent-templates/shared/MEMORY.md +19 -0
  8. package/agent-templates/shared/SOUL.md +43 -0
  9. package/agent-templates/shared/TOOLS.md +85 -0
  10. package/agent-templates/shared/USER.md +23 -0
  11. package/agent-templates/system-skills/app-builder/README.md +8 -0
  12. package/agent-templates/system-skills/app-builder/SKILL.md +678 -0
  13. package/agent-templates/system-skills/app-builder/buda.app.schema.json +165 -0
  14. package/agent-templates/system-skills/app-builder/evals/evals.json +23 -0
  15. package/agent-templates/system-skills/app-builder/scripts/check-preview-url.mjs +184 -0
  16. package/agent-templates/system-skills/buda-automations/SKILL.md +175 -0
  17. package/agent-templates/system-skills/buda-automations/scripts/create-automation.sh +226 -0
  18. package/agent-templates/system-skills/buda-automations/scripts/delete-automation.sh +73 -0
  19. package/agent-templates/system-skills/buda-automations/scripts/list-automations.sh +77 -0
  20. package/agent-templates/system-skills/buda-far/SKILL.md +221 -0
  21. package/agent-templates/system-skills/buda-far/far_gen.py +1499 -0
  22. package/agent-templates/system-skills/buda-machine-info/SKILL.md +67 -0
  23. package/agent-templates/system-skills/buda-machine-info/scripts/get-info.sh +25 -0
  24. package/agent-templates/system-skills/buda-machine-info/scripts/send-file.mjs +62 -0
  25. package/agent-templates/system-skills/buda-web-preview/SKILL.md +462 -0
  26. package/agent-templates/system-skills/find-skills/SKILL.md +142 -0
  27. package/agent-templates/system-skills/skill-creator/LICENSE.txt +202 -0
  28. package/agent-templates/system-skills/skill-creator/SKILL.md +479 -0
  29. package/agent-templates/system-skills/skill-creator/agents/analyzer.md +274 -0
  30. package/agent-templates/system-skills/skill-creator/agents/comparator.md +202 -0
  31. package/agent-templates/system-skills/skill-creator/agents/grader.md +223 -0
  32. package/agent-templates/system-skills/skill-creator/assets/eval_review.html +146 -0
  33. package/agent-templates/system-skills/skill-creator/eval-viewer/generate_review.py +471 -0
  34. package/agent-templates/system-skills/skill-creator/eval-viewer/viewer.html +1325 -0
  35. package/agent-templates/system-skills/skill-creator/references/schemas.md +430 -0
  36. package/agent-templates/system-skills/skill-creator/scripts/__init__.py +0 -0
  37. package/agent-templates/system-skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  38. package/agent-templates/system-skills/skill-creator/scripts/generate_report.py +326 -0
  39. package/agent-templates/system-skills/skill-creator/scripts/improve_description.py +248 -0
  40. package/agent-templates/system-skills/skill-creator/scripts/package_skill.py +136 -0
  41. package/agent-templates/system-skills/skill-creator/scripts/quick_validate.py +103 -0
  42. package/agent-templates/system-skills/skill-creator/scripts/run_eval.py +310 -0
  43. package/agent-templates/system-skills/skill-creator/scripts/run_loop.py +332 -0
  44. package/agent-templates/system-skills/skill-creator/scripts/utils.py +47 -0
  45. package/dist/cli.js +2747 -180
  46. package/package.json +14 -6
  47. package/src/bunny-daemon.ts +0 -52
  48. package/src/cli.ts +0 -97
  49. package/src/cloud-client.ts +0 -134
  50. package/src/config.ts +0 -122
  51. package/src/logger.ts +0 -36
  52. package/src/relay.ts +0 -96
  53. package/src/runtime.ts +0 -59
  54. package/src/types.ts +0 -37
  55. package/tsconfig.json +0 -44
  56. package/tsup.config.ts +0 -20
@@ -0,0 +1,165 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://buda.dev/schemas/buda.app.json",
4
+ "title": "Buda App Definition",
5
+ "description": "Declares to the Buda sandbox how to install, run dev, build, start, and optionally deploy a web app.",
6
+ "type": "object",
7
+ "required": ["name", "version", "type", "description", "framework"],
8
+ "additionalProperties": false,
9
+ "if": {
10
+ "required": ["framework"],
11
+ "properties": { "framework": { "const": "static" } }
12
+ },
13
+ "then": {
14
+ "description": "Static apps need no build tooling: drop files in public/ and omit scripts/port."
15
+ },
16
+ "else": {
17
+ "required": ["port", "scripts"]
18
+ },
19
+ "properties": {
20
+ "name": {
21
+ "type": "string",
22
+ "pattern": "^[a-z][a-z0-9_]*$",
23
+ "description": "Machine-friendly app identifier in snake_case."
24
+ },
25
+ "version": {
26
+ "type": "string",
27
+ "pattern": "^\\d+\\.\\d+\\.\\d+",
28
+ "description": "Semantic version of the app definition."
29
+ },
30
+ "type": {
31
+ "type": "string",
32
+ "enum": ["web"],
33
+ "description": "App type. Currently only web is supported."
34
+ },
35
+ "description": {
36
+ "type": "string",
37
+ "minLength": 1,
38
+ "maxLength": 200,
39
+ "description": "Short human-readable description of the app."
40
+ },
41
+ "framework": {
42
+ "type": "string",
43
+ "enum": ["static", "vite", "next"],
44
+ "description": "Framework identifier used by the Buda sandbox to apply framework-specific behavior. Use \"static\" for plain HTML/CSS/JS with no build step (files served from public/)."
45
+ },
46
+ "port": {
47
+ "type": "integer",
48
+ "minimum": 1,
49
+ "maximum": 65535,
50
+ "description": "Dev server port number."
51
+ },
52
+ "scripts": {
53
+ "type": "object",
54
+ "required": ["install", "dev", "build", "start"],
55
+ "additionalProperties": false,
56
+ "description": "Shell commands aligned with package.json scripts.",
57
+ "properties": {
58
+ "install": {
59
+ "type": "string",
60
+ "description": "Install dependencies command (e.g. bun install)."
61
+ },
62
+ "dev": {
63
+ "type": "string",
64
+ "description": "Start the development server."
65
+ },
66
+ "build": {
67
+ "type": "string",
68
+ "description": "Production build command."
69
+ },
70
+ "start": {
71
+ "type": "string",
72
+ "description": "Start the production server or preview."
73
+ },
74
+ "lint": {
75
+ "type": "string",
76
+ "description": "Lint command (optional)."
77
+ }
78
+ }
79
+ },
80
+ "env": {
81
+ "type": "object",
82
+ "additionalProperties": {
83
+ "type": "string"
84
+ },
85
+ "description": "Default environment variables for the app."
86
+ },
87
+ "deploy": {
88
+ "type": "object",
89
+ "additionalProperties": true,
90
+ "description": "Optional deployment overrides. Buda supplies Cloudflare Workers for Platforms defaults when omitted.",
91
+ "properties": {
92
+ "provider": {
93
+ "type": "string",
94
+ "enum": ["cloudflare-workers-for-platforms"],
95
+ "description": "Deployment provider. Defaults to Cloudflare Workers for Platforms."
96
+ },
97
+ "assets": {
98
+ "type": "string",
99
+ "description": "Static assets directory relative to the app folder. Defaults to public."
100
+ },
101
+ "entry": {
102
+ "type": "string",
103
+ "description": "Optional Worker module entry relative to the app folder. When omitted, Buda generates a static-assets Worker."
104
+ },
105
+ "compatibilityDate": {
106
+ "type": "string",
107
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
108
+ "description": "Cloudflare Worker compatibility date. Defaults to the Buda platform default."
109
+ },
110
+ "notFoundHandling": {
111
+ "type": "string",
112
+ "enum": ["single-page-application", "404-page", "none"],
113
+ "description": "Static asset fallback behavior. Defaults to single-page-application."
114
+ },
115
+ "scriptName": {
116
+ "type": "string",
117
+ "description": "Optional Cloudflare User Worker script name override. Buda derives a Space-scoped name when omitted."
118
+ }
119
+ }
120
+ }
121
+ },
122
+ "examples": [
123
+ {
124
+ "name": "my_page",
125
+ "version": "1.0.0",
126
+ "type": "web",
127
+ "description": "A plain static HTML page (no build step)",
128
+ "framework": "static"
129
+ },
130
+ {
131
+ "name": "my_app",
132
+ "version": "1.0.0",
133
+ "type": "web",
134
+ "description": "A Next.js web application",
135
+ "framework": "next",
136
+ "port": 3000,
137
+ "scripts": {
138
+ "install": "bun install",
139
+ "dev": "./node_modules/.bin/next dev",
140
+ "build": "./node_modules/.bin/next build",
141
+ "start": "./node_modules/.bin/next start"
142
+ },
143
+ "env": {
144
+ "NODE_ENV": "development"
145
+ }
146
+ },
147
+ {
148
+ "name": "my_app",
149
+ "version": "1.0.0",
150
+ "type": "web",
151
+ "description": "A Vite + React application",
152
+ "framework": "vite",
153
+ "port": 5173,
154
+ "scripts": {
155
+ "install": "bun install",
156
+ "dev": "./node_modules/.bin/vite --host 0.0.0.0",
157
+ "build": "./node_modules/.bin/tsc -b && ./node_modules/.bin/vite build",
158
+ "start": "./node_modules/.bin/vite preview --host 0.0.0.0"
159
+ },
160
+ "env": {
161
+ "NODE_ENV": "development"
162
+ }
163
+ }
164
+ ]
165
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "skill_name": "web-app-builder",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "Build me a small web app for tracking freelance invoices. I want to add clients, enter invoice amount/status, and see totals for paid vs unpaid. Use whatever frontend stack makes sense and give me a preview link.",
7
+ "expected_output": "Creates a working web app project with sensible stack selection, invoice/client UI, stateful interactions or local storage, build verification, and a live preview link.",
8
+ "files": []
9
+ },
10
+ {
11
+ "id": 2,
12
+ "prompt": "I already have a Vite React app in /agent/acme-dashboard. Add a responsive analytics dashboard page with cards, a filter, and a table. Don't rewrite the whole app.",
13
+ "expected_output": "Inspects existing project, preserves structure, adds dashboard components/styles, runs relevant verification, and summarizes changed files.",
14
+ "files": []
15
+ },
16
+ {
17
+ "id": 3,
18
+ "prompt": "做一个简单的网页应用:番茄钟 + 待办事项,能开始/暂停计时,任务可以勾选完成。要能在浏览器里打开。",
19
+ "expected_output": "Builds a browser-previewable pomodoro/todo app, likely static or Vite depending on complexity, with start/pause timer and task completion interactions.",
20
+ "files": []
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+
3
+ const FALLBACK_ORIGIN = "https://buda.im/";
4
+
5
+ function printResult(label, value) {
6
+ const rendered = typeof value === "boolean" ? (value ? "yes" : "no") : value;
7
+ console.log(`${label}: ${rendered}`);
8
+ }
9
+
10
+ function parseUrl(input, label) {
11
+ try {
12
+ const url = new URL(input);
13
+ if (!/^https?:$/.test(url.protocol)) {
14
+ throw new Error(`Only http/https URLs are supported, got ${url.protocol}`);
15
+ }
16
+ return url;
17
+ } catch (error) {
18
+ throw new Error(`${label} is not a valid URL: ${error.message}`);
19
+ }
20
+ }
21
+
22
+ function extractPort(url) {
23
+ if (url.port) return Number(url.port);
24
+
25
+ // Buda preview URLs usually encode the local port as the hostname prefix:
26
+ // https://5173-t326d10d005fe029.budaapps.com -> 5173
27
+ const previewPort = url.hostname.match(/^(\d{2,5})-/)?.[1];
28
+ if (previewPort) return Number(previewPort);
29
+
30
+ return null;
31
+ }
32
+
33
+ function getHeader(headers, name) {
34
+ return headers.get(name) ?? "";
35
+ }
36
+
37
+ function evaluateCors(headers, requestedOrigin) {
38
+ const allowOrigin = getHeader(headers, "access-control-allow-origin");
39
+ const allowMethods = getHeader(headers, "access-control-allow-methods");
40
+ const allowCredentials = getHeader(headers, "access-control-allow-credentials");
41
+
42
+ return {
43
+ allowOrigin,
44
+ allowMethods,
45
+ allowCredentials,
46
+ passes:
47
+ allowOrigin === "*" ||
48
+ allowOrigin === requestedOrigin.origin ||
49
+ allowOrigin === requestedOrigin.href.replace(/\/$/, ""),
50
+ };
51
+ }
52
+
53
+ async function request(method, url, originUrl) {
54
+ const response = await fetch(url, {
55
+ method,
56
+ redirect: "manual",
57
+ headers: {
58
+ Origin: originUrl.origin,
59
+ },
60
+ });
61
+
62
+ return {
63
+ method,
64
+ url,
65
+ status: response.status,
66
+ ok: response.ok,
67
+ unauthorized: response.status === 401 || response.status === 403,
68
+ redirected: response.status >= 300 && response.status < 400,
69
+ location: getHeader(response.headers, "location"),
70
+ cors: evaluateCors(response.headers, originUrl),
71
+ };
72
+ }
73
+
74
+ function reportHttpResult(result) {
75
+ printResult(`${result.method} ${result.url} status`, result.status);
76
+ printResult(`${result.method} ${result.url} ok`, result.ok);
77
+ printResult(`${result.method} ${result.url} unauthorized`, result.unauthorized);
78
+
79
+ if (result.redirected) {
80
+ printResult(`${result.method} ${result.url} redirect location`, result.location || "(missing)");
81
+ }
82
+
83
+ printResult(
84
+ `${result.method} ${result.url} CORS allow-origin`,
85
+ result.cors.allowOrigin || "(none)",
86
+ );
87
+ printResult(
88
+ `${result.method} ${result.url} CORS allow-methods`,
89
+ result.cors.allowMethods || "(none)",
90
+ );
91
+ printResult(
92
+ `${result.method} ${result.url} CORS allow-credentials`,
93
+ result.cors.allowCredentials || "(none)",
94
+ );
95
+ printResult(`${result.method} ${result.url} CORS passes for origin`, result.cors.passes);
96
+ }
97
+
98
+ export async function checkUrl(targetUrlInput, options = {}) {
99
+ if (!targetUrlInput) {
100
+ throw new Error("checkUrl(url, options) requires a target URL.");
101
+ }
102
+
103
+ const targetUrl = parseUrl(targetUrlInput, "Target URL");
104
+ const originUrl = parseUrl(
105
+ options.origin ?? process.env.NEXT_PUBLIC_APP_URL ?? FALLBACK_ORIGIN,
106
+ "Origin URL",
107
+ );
108
+ const port = extractPort(targetUrl);
109
+ const localhostUrl = port === null ? null : `http://localhost:${port}/`;
110
+
111
+ const result = {
112
+ targetUrl: targetUrl.href,
113
+ origin: originUrl.origin,
114
+ port,
115
+ remoteHead: null,
116
+ localhostUrl,
117
+ localhostHead: null,
118
+ };
119
+
120
+ result.remoteHead = await request("HEAD", targetUrl.href, originUrl);
121
+
122
+ if (localhostUrl) {
123
+ result.localhostHead = await request("HEAD", localhostUrl, originUrl);
124
+ }
125
+
126
+ return result;
127
+ }
128
+
129
+ export function reportCheckResult(result) {
130
+ printResult("target URL", result.targetUrl);
131
+ printResult("origin", result.origin);
132
+ printResult("extracted port", result.port ?? "not found");
133
+
134
+ console.log("\nChecking remote URL with HEAD first...");
135
+ reportHttpResult(result.remoteHead);
136
+
137
+ if (result.remoteHead.status === 200) {
138
+ console.log("Remote HEAD returned 200.");
139
+ } else if (result.remoteHead.unauthorized) {
140
+ console.log("Remote URL returned unauthorized on HEAD.");
141
+ } else {
142
+ console.log(`Remote HEAD did not return 200; got ${result.remoteHead.status}.`);
143
+ }
144
+
145
+ if (!result.localhostUrl) {
146
+ console.log("\nSkipping localhost check because no port could be extracted.");
147
+ return;
148
+ }
149
+
150
+ console.log("\nChecking localhost:port with HEAD first...");
151
+ reportHttpResult(result.localhostHead);
152
+
153
+ if (result.localhostHead.status === 200) {
154
+ console.log("localhost:port is working with HEAD 200.");
155
+ } else if (result.localhostHead.unauthorized) {
156
+ console.log("localhost:port returned unauthorized on HEAD.");
157
+ } else {
158
+ console.log(`localhost:port HEAD did not return 200; got ${result.localhostHead.status}.`);
159
+ }
160
+ }
161
+
162
+ async function main() {
163
+ const targetInput = process.argv[2];
164
+ const originInput = process.argv[3];
165
+
166
+ if (!targetInput) {
167
+ console.error("Usage: node scripts/check-url.mjs <url> [origin]");
168
+ console.error(
169
+ "Example: node scripts/check-url.mjs https://5173-example.budaapps.com https://buda.im/",
170
+ );
171
+ process.exitCode = 2;
172
+ return;
173
+ }
174
+
175
+ const result = await checkUrl(targetInput, { origin: originInput });
176
+ reportCheckResult(result);
177
+ }
178
+
179
+ if (import.meta.url === `file://${process.argv[1]}`) {
180
+ main().catch((error) => {
181
+ console.error(error.message);
182
+ process.exitCode = 1;
183
+ });
184
+ }
@@ -0,0 +1,175 @@
1
+ ---
2
+ name: buda-automations
3
+ description: "Use this skill when the user wants to schedule, list, or delete recurring agent tasks in Buda. Trigger on explicit requests (\"create a daily automation\", \"set up a cron job\", \"show my scheduled tasks\", \"delete this automation\") and indirect signals (\"every morning\", \"once a day\", \"remind me weekly\", \"check every hour\", \"run this automatically\"). Also trigger when the user describes a repeated or timed action without using the word \"automation\"."
4
+ compatibility: Requires the built-in Buda automation tools (create_automation, list_automations, delete_automation) — auto-injected in Buda sandboxes.
5
+ metadata:
6
+ author: buda
7
+ version: "1.0"
8
+ ---
9
+
10
+ # Automation Management
11
+
12
+ Manage scheduled automations in the user's Buda workspace. An automation is a
13
+ recurring (or one-time) task that triggers an agent session with a predefined
14
+ prompt on a schedule.
15
+
16
+ ## When to use this skill
17
+
18
+ - User asks to **create** a scheduled/recurring task ("Check the news for me every morning at 9am")
19
+ - User asks to **list** existing automations ("What scheduled tasks do I have?")
20
+ - User asks to **delete** an automation ("Remove the xxx scheduled task, I don't need it anymore")
21
+ - Any mention of cron, interval, timer, reminder, periodic, recurring, daily,
22
+ weekly, hourly, or similar scheduling concepts
23
+
24
+ ## How it works
25
+
26
+ Use the built-in Buda tools (`create_automation`, `list_automations`,
27
+ `delete_automation`) — do **not** run the shell scripts in `scripts/`.
28
+ The tools are always available in the sandbox and handle auth automatically.
29
+
30
+ All operations are scoped to the current agent's workspace — you cannot
31
+ accidentally touch other workspaces.
32
+
33
+ **CRITICAL — timezone rule**: When using `--schedule-type cron`, write the cron expression in the user's local time and pass `--timezone` with the user's IANA timezone. The scheduler interprets the cron expression in that timezone and stores `nextRunAt` as an absolute timestamp.
34
+
35
+ Example: user says "every day at 14:00" in Asia/Shanghai → `--cron "0 14 * * *" --timezone "Asia/Shanghai"`.
36
+
37
+ ## Subscription Limits
38
+
39
+ Buda has different subscription plans that restrict how automations can be used. When a user tries to create an automation that exceeds their plan's limits, the API will return an error. You should be aware of these limits:
40
+
41
+ | Plan | Cron Support (`allowCron`) | Max Automations (`maxCount`) | Minimum Interval (`minInterval`) |
42
+ |------|----------------------------|------------------------------|----------------------------------|
43
+ | **Free** | No (`false`) | 5 | 120 minutes |
44
+ | **Plus** | Yes (`true`) | 20 | 30 minutes |
45
+ | **Pro** | Yes (`true`) | 100 | 5 minutes |
46
+ | **Enterprise**| Yes (`true`) | Unlimited | 1 minute |
47
+
48
+ If a user hits a plan limit (e.g., they ask for a cron schedule or an interval less than 120 minutes on a Free plan), **do not just report the error**. You MUST offer actionable solutions:
49
+ 1. **Suggest an alternative that fits their current plan** (e.g., "Since you are on the Free plan, we can't use a specific time of day (cron). Would you like me to set this up to run every 120 minutes instead?").
50
+ 2. **Offer an upgrade path** (e.g., "If you need this to run at exactly 9am every day, you can upgrade your subscription to Plus or Pro in your Workspace Settings -> Billing").
51
+
52
+ ## Available operations
53
+
54
+ | Operation | Tool | Description |
55
+ |-----------|-----------------------|--------------------------------------|
56
+ | List | `list_automations` | List all automations in workspace |
57
+ | Create | `create_automation` | Create a new scheduled automation |
58
+ | Delete | `delete_automation` | Delete an automation by ID |
59
+
60
+ ## Step-by-step instructions
61
+
62
+ ### Creating an automation
63
+
64
+ 1. Parse the user's request to determine:
65
+ - **What** the task should do → becomes `taskTemplate.prompt` (the `name` is also used as `taskTemplate.title` automatically)
66
+ - **When** it should run → becomes `scheduleType` + schedule config
67
+ - **How to deliver result**:
68
+ - Default to current automation session (`delivery.mode: "session"`)
69
+ - Do **not** use `delivery.mode: "notify"` just because the task is scheduled, recurring, or uses cron
70
+ - Use `delivery.mode: "notify"` only when the user explicitly asks to notify/push/send the result to a connected channel
71
+ - Set `delivery.sessionId` to target the exact connected chat/group/DM whenever it is available
72
+ - `delivery.channelId` is a legacy fallback only; do not prefer it over `delivery.sessionId`
73
+ 2. Choose the right schedule type (see [Schedule types](#schedule-types) below)
74
+ 3. Call the `create_automation` tool. Example for an interval schedule:
75
+ - `name`: "Daily News Briefing"
76
+ - `scheduleType`: "interval"
77
+ - `intervalMinutes`: 1440
78
+ - `taskTemplate.prompt`: "Search today's financial news and generate a summary"
79
+ - `taskTemplate.delivery.mode`: "session"
80
+
81
+ Example for a cron schedule:
82
+ - `name`: "Morning News"
83
+ - `scheduleType`: "cron"
84
+ - `cronExpression`: "0 9 * * *"
85
+ - `timezone`: "Asia/Shanghai"
86
+ - `taskTemplate.prompt`: "Search today's financial news and generate a summary"
87
+ - `taskTemplate.delivery.mode`: "session"
88
+
89
+ Example only when the user explicitly asks to push results to a connected chat/group/DM:
90
+ - `name`: "Morning News to Team Group"
91
+ - `scheduleType`: "cron"
92
+ - `cronExpression`: "0 9 * * *"
93
+ - `timezone`: "Asia/Shanghai"
94
+ - `taskTemplate.prompt`: "Search today's financial news and generate a summary"
95
+ - `taskTemplate.delivery.mode`: "notify"
96
+ - `taskTemplate.delivery.sessionId`: "tsk_xxxxxxxxxxxxxxxx"
97
+
98
+ 4. Report the result to the user, including the automation name and next run time.
99
+
100
+ ### Listing automations
101
+
102
+ Call the `list_automations` tool. Present the results in a readable format including name, schedule description, enabled status, and next run time.
103
+
104
+ ### Updating an automation
105
+
106
+ To update an automation, delete the old one and create a new one with the updated settings. Always pass `timezone` when using `scheduleType: "cron"`.
107
+
108
+ ### Deleting an automation
109
+
110
+ 1. If the user refers to an automation by name, first call `list_automations` to find the matching ID.
111
+ 2. Confirm with the user before deleting.
112
+ 3. Call the `delete_automation` tool with the automation `id`.
113
+ 4. Confirm deletion to the user.
114
+
115
+ ## Schedule types
116
+
117
+ | Type | When to use | Required flag | Example |
118
+ |------------|------------------------------------------------|--------------------------|--------------------------------------------|
119
+ | `interval` | Simple recurring ("every N minutes/hours") | `--interval-minutes N` | `--interval-minutes 60` (hourly) |
120
+ | `cron` | Complex schedules ("weekdays at 9am") | `--cron "expr"` | `--cron "0 9 * * 1-5"` with `--timezone "Asia/Shanghai"` |
121
+ | `once` | One-time future execution | `--scheduled-at ISO8601` | `--scheduled-at 2026-03-25T10:00:00Z` |
122
+
123
+ **Important**: For `cron` type, the cron expression must be in the user's local time. Always pass `--timezone` with the user's IANA timezone. If the user's timezone is unknown, ask them.
124
+
125
+ ### Natural language → schedule mapping
126
+
127
+ | User says | Schedule type | Config (Asia/Shanghai example) |
128
+ |----------------------------|---------------|-------------------------------------------------------------|
129
+ | "every hour" | interval | `--interval-minutes 60` |
130
+ | "daily" / "once a day" | interval | `--interval-minutes 1440` |
131
+ | "daily at 9am" | cron | `--cron "0 9 * * *" --timezone "Asia/Shanghai"` |
132
+ | "weekdays at 9am" | cron | `--cron "0 9 * * 1-5" --timezone "Asia/Shanghai"` |
133
+ | "every Monday" | cron | `--cron "0 9 * * 1" --timezone "Asia/Shanghai"` |
134
+ | "every 5 minutes" | interval | `--interval-minutes 5` |
135
+ | "1st of each month" | cron | `--cron "0 9 1 * *" --timezone "Asia/Shanghai"` |
136
+ | "tomorrow at 3pm" | once | `--scheduled-at <ISO8601>` |
137
+
138
+ ### Common cron expressions
139
+
140
+ ```
141
+ * * * * * Every minute
142
+ */5 * * * * Every 5 minutes
143
+ 0 * * * * Every hour (at :00)
144
+ 0 9 * * * Daily at 9:00 AM in the specified timezone
145
+ 0 9 * * 1-5 Weekdays at 9:00 AM in the specified timezone
146
+ 0 9 * * 1 Every Monday at 9:00 AM in the specified timezone
147
+ 0 0 * * * Daily at midnight in the specified timezone
148
+ 0 9 1 * * 1st of each month at 9:00 AM in the specified timezone
149
+ ```
150
+
151
+ ## Error handling
152
+
153
+ - If the tool returns a plan limit error (e.g., `FORBIDDEN` about plan limits), you **must** offer an actionable solution (e.g., fallback to interval, or suggest upgrading in Workspace Settings -> Billing).
154
+ - If the tool returns a validation error (e.g., `BAD_REQUEST`), explain exactly which field is wrong and suggest a fix.
155
+ - For invalid cron expressions, suggest a valid alternative.
156
+
157
+ ## Language & Terminology
158
+
159
+ When communicating with users in Chinese, it's critical to use the exact UI terminology found in the product to avoid confusion.
160
+
161
+ - **Automation / Scheduled Task**: Translate as "定时任务" (do not use "自动化" directly if confusing).
162
+ - **Plan (Free / Plus / Pro / Enterprise)**: Translate as "订阅" (e.g. "Free plan" -> "免费版", "Upgrade plan" -> "升级订阅").
163
+ - **Cron**: Can be kept as "Cron 计划" or translated naturally as "指定时间运行".
164
+
165
+ Example Chinese response for a Free plan limit:
166
+ "因为您当前处于**免费订阅 (Free)**,不支持指定具体时间(Cron)执行。您是否希望我帮您设置为每 xxx 分钟执行一次?如果您需要每天固定时间执行,可以前往**工作区设置 -> 账单**中升级您的订阅至 Plus 或更高版本。"
167
+
168
+ ## Tips
169
+
170
+ - When the user's intent is ambiguous about timing, ask for clarification
171
+ before creating. For example, "check the news for me daily" — ask whether
172
+ they want a specific time (cron) or just every 24 hours (interval).
173
+ - The `prompt` field is what the agent will receive as its task when the
174
+ automation triggers. Make it specific and actionable.
175
+ - Automation names should be descriptive so the user can identify them later.