mercury-agent 0.5.0 → 0.5.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.
- package/README.md +452 -451
- package/container/Dockerfile +127 -127
- package/container/Dockerfile.base +109 -109
- package/container/Dockerfile.power +17 -17
- package/container/agent-package.json +8 -8
- package/container/build.sh +54 -54
- package/docs/ROADMAP.md +3 -4
- package/docs/archive/summarization/2026-07-02-recent-dev-summary.md +71 -0
- package/docs/auth/dashboard.md +28 -28
- package/docs/auth/overview.md +109 -109
- package/docs/auth/whatsapp.md +173 -173
- package/docs/authoring-profiles.md +174 -174
- package/docs/configuration.md +54 -54
- package/docs/container-lifecycle.md +349 -349
- package/docs/{bugs/bwrap-privileged-linux-docker.md → debug/major/2026-07-02-bwrap-privileged-linux-docker.md} +21 -1
- package/docs/{bugs/e2big-prompt-delivery-fix.md → debug/major/2026-07-02-e2big-prompt-delivery-fix.md} +27 -1
- package/docs/{bugs/registry-pull-no-local-fallback.md → debug/major/2026-07-02-registry-pull-no-local-fallback.md} +27 -1
- package/docs/debug/summarization/2026-07-02-common-bug-patterns.md +72 -0
- package/docs/deployment.md +199 -199
- package/docs/extensions.md +375 -375
- package/docs/graceful-shutdown.md +62 -62
- package/docs/kb-distillation.md +77 -77
- package/docs/media/overview.md +140 -140
- package/docs/media/whatsapp.md +171 -171
- package/docs/memory.md +137 -137
- package/docs/permissions.md +217 -217
- package/docs/pipeline.md +228 -228
- package/docs/prd-chat-memory.md +76 -76
- package/docs/prd-config-load.md +82 -82
- package/docs/rate-limiting.md +229 -229
- package/docs/runbooks/publish-checklist.md +9 -1
- package/docs/scheduler.md +288 -288
- package/docs/setup-discord.md +100 -100
- package/docs/setup-slack.md +119 -119
- package/docs/setup-whatsapp.md +94 -94
- package/docs/subagents.md +166 -166
- package/docs/web-search.md +62 -62
- package/examples/extensions/README.md +12 -12
- package/examples/extensions/charts/index.ts +13 -13
- package/examples/extensions/charts/skill/SKILL.md +98 -98
- package/examples/extensions/gws/README.md +52 -52
- package/examples/extensions/gws/skill/SKILL.md +57 -57
- package/examples/extensions/gws/skill/references/calendar.md +101 -101
- package/examples/extensions/gws/skill/references/docs.md +65 -65
- package/examples/extensions/gws/skill/references/drive.md +79 -79
- package/examples/extensions/gws/skill/references/gmail.md +85 -85
- package/examples/extensions/gws/skill/references/sheets.md +60 -60
- package/examples/extensions/napkin/skill/SKILL.md +728 -728
- package/examples/extensions/pdf/skill/LICENSE.txt +30 -30
- package/examples/extensions/pdf/skill/SKILL.md +314 -314
- package/examples/extensions/pdf/skill/forms.md +294 -294
- package/examples/extensions/pdf/skill/reference.md +611 -611
- package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -65
- package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -11
- package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -33
- package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -37
- package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -122
- package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -115
- package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -98
- package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -107
- package/examples/extensions/permission-guard/index.ts +65 -65
- package/examples/extensions/pinchtab/skill/SKILL.md +224 -224
- package/examples/extensions/pinchtab/skill/TRUST.md +69 -69
- package/examples/extensions/pinchtab/skill/references/api.md +297 -297
- package/examples/extensions/pinchtab/skill/references/env.md +45 -45
- package/examples/extensions/pinchtab/skill/references/profiles.md +107 -107
- package/examples/extensions/tradestation/host/refresh.ts +102 -102
- package/examples/extensions/tradestation/index.ts +153 -153
- package/examples/extensions/tradestation/skill/SKILL.md +67 -67
- package/examples/extensions/voice-synth/index.ts +94 -94
- package/examples/extensions/voice-synth/skill/SKILL.md +38 -38
- package/examples/extensions/voice-transcribe/requirements.txt +8 -8
- package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -179
- package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -53
- package/examples/extensions/yahoo-mail/cli/package.json +13 -13
- package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -78
- package/package.json +106 -106
- package/resources/agents/explore.md +50 -50
- package/resources/agents/worker.md +24 -24
- package/resources/connection-env-vars.json +25 -25
- package/resources/pi-extensions/subagent/agents.ts +126 -126
- package/resources/pi-extensions/subagent/index.ts +964 -964
- package/resources/profiles/coding/AGENTS.md +43 -43
- package/resources/profiles/coding/mercury-profile.yaml +15 -15
- package/resources/profiles/general/AGENTS.md +31 -31
- package/resources/profiles/general/mercury-profile.yaml +15 -15
- package/resources/profiles/research/AGENTS.md +40 -40
- package/resources/profiles/research/mercury-profile.yaml +15 -15
- package/resources/skills/config/SKILL.md +25 -25
- package/resources/skills/context/SKILL.md +33 -33
- package/resources/skills/conversation-recap/SKILL.md +19 -19
- package/resources/skills/mutes/SKILL.md +31 -31
- package/resources/skills/permissions/SKILL.md +19 -19
- package/resources/skills/preferences/SKILL.md +31 -31
- package/resources/skills/recall/SKILL.md +24 -24
- package/resources/skills/roles/SKILL.md +18 -18
- package/resources/skills/spaces/SKILL.md +18 -18
- package/resources/skills/tasks/SKILL.md +45 -45
- package/resources/templates/AGENTS.md +157 -157
- package/resources/templates/env.template +38 -38
- package/resources/templates/mercury.example.yaml +99 -99
- package/src/agent/container-entry.ts +1 -1
- package/src/agent/container-runner.ts +1345 -1346
- package/src/cli/mercury.ts +39 -7
- package/src/cli/mrctl.ts +636 -636
- package/src/config-file.ts +540 -540
- package/src/config.ts +339 -339
- package/src/core/api.ts +125 -125
- package/src/core/caller-token.ts +101 -101
- package/src/core/permissions.ts +228 -228
- package/src/core/profiles.ts +271 -271
- package/src/core/routes/capability.ts +70 -70
- package/src/core/routes/index.ts +15 -15
- package/src/core/runtime.ts +1530 -1530
- package/src/dashboard/index.html +729 -729
- package/src/extensions/api.ts +273 -273
- package/src/extensions/loader.ts +286 -286
- package/src/extensions/types.ts +517 -517
- package/src/main.ts +605 -605
- package/docs/pending-updates/applicative-profiles.md +0 -15
- package/docs/pending-updates/caller-bound-capability-token.md +0 -15
- package/docs/pending-updates/dm-auto-space.md +0 -15
- /package/docs/archive/{2026-07-01-applicative-profiles.md → applicative-profiles/2026-07-01-applicative-profiles.md} +0 -0
- /package/docs/archive/{2026-07-01-caller-bound-capability-token.md → applicative-profiles/2026-07-01-caller-bound-capability-token.md} +0 -0
- /package/docs/archive/{2026-07-01-dm-auto-space.md → applicative-profiles/2026-07-01-dm-auto-space.md} +0 -0
package/docs/scheduler.md
CHANGED
|
@@ -1,288 +1,288 @@
|
|
|
1
|
-
# Scheduler
|
|
2
|
-
|
|
3
|
-
Mercury includes a task scheduler for automated prompts. Tasks can run on **cron schedules** (recurring) or **at schedules** (one-shot).
|
|
4
|
-
|
|
5
|
-
## Task Types
|
|
6
|
-
|
|
7
|
-
### Cron Tasks (Recurring)
|
|
8
|
-
|
|
9
|
-
Run on a cron schedule, repeating indefinitely:
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
# Daily standup at 9am
|
|
13
|
-
mrctl tasks create --cron "0 9 * * *" --prompt "Good morning! What's on the agenda today?"
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
### At Tasks (One-Shot)
|
|
17
|
-
|
|
18
|
-
Run once at a specific time, then auto-delete:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
# Reminder in 2 hours
|
|
22
|
-
mrctl tasks create --at "2026-03-02T16:00:00Z" --prompt "Time for the team meeting!"
|
|
23
|
-
|
|
24
|
-
# Schedule a future check
|
|
25
|
-
mrctl tasks create --at "2026-03-15T09:00:00Z" --prompt "Follow up on the Q1 report"
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
At-tasks are useful for:
|
|
29
|
-
- **Reminders** — one-time notifications
|
|
30
|
-
- **Delayed actions** — schedule something for later
|
|
31
|
-
- **Follow-ups** — check back on something at a specific time
|
|
32
|
-
|
|
33
|
-
The `at` timestamp must be:
|
|
34
|
-
- ISO 8601 format (e.g., `2026-03-02T14:00:00Z`)
|
|
35
|
-
- In the future
|
|
36
|
-
|
|
37
|
-
## Silent Tasks
|
|
38
|
-
|
|
39
|
-
Tasks can be marked as **silent** to execute without posting results to the chat. This is useful for:
|
|
40
|
-
|
|
41
|
-
- **Maintenance tasks** — cleanup, archiving, or housekeeping
|
|
42
|
-
- **Health checks** — periodic monitoring without noise
|
|
43
|
-
- **Background updates** — knowledge base updates, data syncing
|
|
44
|
-
|
|
45
|
-
The task executes normally but no message is sent to the space.
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
# Create a silent cron task
|
|
49
|
-
mrctl tasks create --cron "0 3 * * *" --prompt "Run nightly maintenance" --silent
|
|
50
|
-
|
|
51
|
-
# Create a silent at-task
|
|
52
|
-
mrctl tasks create --at "2026-03-02T03:00:00Z" --prompt "One-time cleanup" --silent
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## How It Works
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
TaskScheduler.start()
|
|
59
|
-
│
|
|
60
|
-
└─► Poll loop (every 5 seconds)
|
|
61
|
-
│
|
|
62
|
-
├─► Query DB for due tasks (active=1, next_run_at <= now)
|
|
63
|
-
│
|
|
64
|
-
├─► For each due task:
|
|
65
|
-
│ │
|
|
66
|
-
│ ├─► [Cron task]
|
|
67
|
-
│ │ ├─► Compute next run time from cron expression
|
|
68
|
-
│ │ ├─► Update next_run_at in DB
|
|
69
|
-
│ │ └─► Execute handler
|
|
70
|
-
│ │
|
|
71
|
-
│ └─► [At task]
|
|
72
|
-
│ ├─► Execute handler
|
|
73
|
-
│ └─► Delete task from DB
|
|
74
|
-
│
|
|
75
|
-
└─► Schedule next poll
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Tasks are processed sequentially within a poll cycle. Each task runs as if the `createdBy` user sent the prompt.
|
|
79
|
-
|
|
80
|
-
**At-task lifecycle:**
|
|
81
|
-
1. Created with a future timestamp
|
|
82
|
-
2. Waits until scheduled time
|
|
83
|
-
3. Executes once
|
|
84
|
-
4. Auto-deletes (regardless of success/failure)
|
|
85
|
-
|
|
86
|
-
## Creating Tasks
|
|
87
|
-
|
|
88
|
-
The agent creates tasks via `mrctl`:
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
# === Cron tasks (recurring) ===
|
|
92
|
-
|
|
93
|
-
# Daily standup at 9am
|
|
94
|
-
mrctl tasks create --cron "0 9 * * *" --prompt "Good morning! What's on the agenda today?"
|
|
95
|
-
|
|
96
|
-
# Weekly summary on Fridays at 5pm
|
|
97
|
-
mrctl tasks create --cron "0 17 * * 5" --prompt "Generate a summary of this week's discussions."
|
|
98
|
-
|
|
99
|
-
# Every 6 hours
|
|
100
|
-
mrctl tasks create --cron "0 */6 * * *" --prompt "Check for any pending items."
|
|
101
|
-
|
|
102
|
-
# Silent nightly cleanup (no chat output)
|
|
103
|
-
mrctl tasks create --cron "0 3 * * *" --prompt "Clean up old temp files" --silent
|
|
104
|
-
|
|
105
|
-
# === At tasks (one-shot) ===
|
|
106
|
-
|
|
107
|
-
# Reminder at a specific time
|
|
108
|
-
mrctl tasks create --at "2026-03-02T14:00:00Z" --prompt "Meeting starts in 15 minutes!"
|
|
109
|
-
|
|
110
|
-
# Delayed follow-up
|
|
111
|
-
mrctl tasks create --at "2026-03-10T09:00:00Z" --prompt "Check if the deployment completed successfully"
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**Note:** You must specify either `--cron` or `--at`, not both.
|
|
115
|
-
|
|
116
|
-
## Managing Tasks
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
# List all tasks in the current space
|
|
120
|
-
mrctl tasks list
|
|
121
|
-
|
|
122
|
-
# Pause a task (stops execution, keeps definition)
|
|
123
|
-
mrctl tasks pause <id>
|
|
124
|
-
|
|
125
|
-
# Resume a paused task
|
|
126
|
-
mrctl tasks resume <id>
|
|
127
|
-
|
|
128
|
-
# Manually trigger a task now
|
|
129
|
-
mrctl tasks run <id>
|
|
130
|
-
|
|
131
|
-
# Delete a task permanently
|
|
132
|
-
mrctl tasks delete <id>
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Cron Format
|
|
136
|
-
|
|
137
|
-
Standard 5-field cron expressions:
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
┌───────────── minute (0-59)
|
|
141
|
-
│ ┌───────────── hour (0-23)
|
|
142
|
-
│ │ ┌───────────── day of month (1-31)
|
|
143
|
-
│ │ │ ┌───────────── month (1-12)
|
|
144
|
-
│ │ │ │ ┌───────────── day of week (0-7, 0 and 7 are Sunday)
|
|
145
|
-
│ │ │ │ │
|
|
146
|
-
* * * * *
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Examples:
|
|
150
|
-
|
|
151
|
-
| Expression | Description |
|
|
152
|
-
|------------|-------------|
|
|
153
|
-
| `0 9 * * *` | Every day at 9:00 AM |
|
|
154
|
-
| `0 9 * * 1-5` | Weekdays at 9:00 AM |
|
|
155
|
-
| `*/15 * * * *` | Every 15 minutes |
|
|
156
|
-
| `0 */6 * * *` | Every 6 hours |
|
|
157
|
-
| `0 17 * * 5` | Fridays at 5:00 PM |
|
|
158
|
-
| `0 0 1 * *` | First day of each month at midnight |
|
|
159
|
-
|
|
160
|
-
Mercury uses [cron-parser](https://www.npmjs.com/package/cron-parser) for parsing.
|
|
161
|
-
|
|
162
|
-
## Task Execution
|
|
163
|
-
|
|
164
|
-
When a task fires:
|
|
165
|
-
|
|
166
|
-
1. The prompt is sent to the space as if from the task creator
|
|
167
|
-
2. Runs through the normal routing (trigger check bypassed for scheduled tasks)
|
|
168
|
-
3. Caller ID is the `createdBy` user
|
|
169
|
-
4. Permissions are checked against the creator's role at execution time
|
|
170
|
-
|
|
171
|
-
Tasks run with `system` caller privileges for the routing layer, but the prompt is attributed to the original creator.
|
|
172
|
-
|
|
173
|
-
## Storage
|
|
174
|
-
|
|
175
|
-
Tasks are stored in SQLite:
|
|
176
|
-
|
|
177
|
-
```sql
|
|
178
|
-
CREATE TABLE tasks (
|
|
179
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
180
|
-
space_id TEXT NOT NULL,
|
|
181
|
-
cron TEXT, -- Cron expression (null for at-tasks)
|
|
182
|
-
at TEXT, -- ISO 8601 timestamp (null for cron-tasks)
|
|
183
|
-
prompt TEXT NOT NULL,
|
|
184
|
-
active INTEGER NOT NULL DEFAULT 1,
|
|
185
|
-
silent INTEGER NOT NULL DEFAULT 0,
|
|
186
|
-
next_run_at INTEGER NOT NULL,
|
|
187
|
-
created_by TEXT NOT NULL,
|
|
188
|
-
created_at INTEGER NOT NULL,
|
|
189
|
-
updated_at INTEGER NOT NULL
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
CREATE INDEX idx_tasks_next ON tasks(active, next_run_at);
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
| Column | Description |
|
|
196
|
-
|--------|-------------|
|
|
197
|
-
| `cron` | Cron expression for recurring tasks (null for at-tasks) |
|
|
198
|
-
| `at` | ISO 8601 timestamp for one-shot tasks (null for cron-tasks) |
|
|
199
|
-
| `silent` | If 1, task runs but doesn't post results to chat |
|
|
200
|
-
|
|
201
|
-
## Permissions
|
|
202
|
-
|
|
203
|
-
Task management requires specific permissions:
|
|
204
|
-
|
|
205
|
-
| Permission | Action |
|
|
206
|
-
|------------|--------|
|
|
207
|
-
| `tasks.list` | View scheduled tasks |
|
|
208
|
-
| `tasks.create` | Create new tasks |
|
|
209
|
-
| `tasks.pause` | Pause a task |
|
|
210
|
-
| `tasks.resume` | Resume a paused task |
|
|
211
|
-
| `tasks.delete` | Delete a task |
|
|
212
|
-
|
|
213
|
-
By default, only `admin` has these permissions. Grant to other roles:
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
mrctl permissions set member prompt,tasks.list
|
|
217
|
-
mrctl permissions set moderator prompt,tasks.list,tasks.pause,tasks.resume
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
## Lifecycle
|
|
221
|
-
|
|
222
|
-
```
|
|
223
|
-
mercury run
|
|
224
|
-
│
|
|
225
|
-
├─► runtime.initialize()
|
|
226
|
-
│
|
|
227
|
-
├─► scheduler.start(handler)
|
|
228
|
-
│ └─► Poll loop begins
|
|
229
|
-
│
|
|
230
|
-
├─► ... running ...
|
|
231
|
-
│
|
|
232
|
-
└─► SIGTERM/SIGINT
|
|
233
|
-
└─► scheduler.stop()
|
|
234
|
-
└─► Poll loop ends (graceful)
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
The scheduler stops cleanly on shutdown — no orphaned timers.
|
|
238
|
-
|
|
239
|
-
## API
|
|
240
|
-
|
|
241
|
-
### `TaskScheduler`
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
const scheduler = new TaskScheduler(db, pollIntervalMs);
|
|
245
|
-
|
|
246
|
-
scheduler.start(handler); // Begin polling
|
|
247
|
-
scheduler.stop(); // Stop polling
|
|
248
|
-
scheduler.computeNextRun(cron, from); // Get next run time for cron tasks
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### Handler Signature
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
type TaskHandler = (task: {
|
|
255
|
-
id: number;
|
|
256
|
-
spaceId: string;
|
|
257
|
-
prompt: string;
|
|
258
|
-
createdBy: string;
|
|
259
|
-
silent: boolean;
|
|
260
|
-
}) => Promise<void>;
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
### Database Methods
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
// Create a cron task
|
|
267
|
-
db.createTask(spaceId, { cron: "0 9 * * *" }, prompt, nextRunAt, createdBy, silent);
|
|
268
|
-
|
|
269
|
-
// Create an at-task
|
|
270
|
-
db.createTask(spaceId, { at: "2026-03-02T14:00:00Z" }, prompt, nextRunAt, createdBy, silent);
|
|
271
|
-
|
|
272
|
-
db.listTasks(spaceId?); // List tasks (optionally filter by space)
|
|
273
|
-
db.getDueTasks(now); // Get tasks ready to run
|
|
274
|
-
db.getTask(id); // Get single task
|
|
275
|
-
db.setTaskActive(id, active); // Pause/resume
|
|
276
|
-
db.deleteTask(id, spaceId); // Delete task (with space check)
|
|
277
|
-
db.deleteTaskById(id); // Delete task (no space check, for scheduler)
|
|
278
|
-
db.updateTaskNextRun(id, nextRunAt); // Update next execution time
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
## Error Handling
|
|
282
|
-
|
|
283
|
-
If a task handler fails:
|
|
284
|
-
- Error is logged
|
|
285
|
-
- Task is not retried in the same cycle
|
|
286
|
-
- **Cron tasks:** `next_run_at` is already updated, so it will run again at the next scheduled time
|
|
287
|
-
- **At tasks:** Still deleted after execution (one-shot behavior preserved)
|
|
288
|
-
- Other tasks in the cycle continue to execute
|
|
1
|
+
# Scheduler
|
|
2
|
+
|
|
3
|
+
Mercury includes a task scheduler for automated prompts. Tasks can run on **cron schedules** (recurring) or **at schedules** (one-shot).
|
|
4
|
+
|
|
5
|
+
## Task Types
|
|
6
|
+
|
|
7
|
+
### Cron Tasks (Recurring)
|
|
8
|
+
|
|
9
|
+
Run on a cron schedule, repeating indefinitely:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Daily standup at 9am
|
|
13
|
+
mrctl tasks create --cron "0 9 * * *" --prompt "Good morning! What's on the agenda today?"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### At Tasks (One-Shot)
|
|
17
|
+
|
|
18
|
+
Run once at a specific time, then auto-delete:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Reminder in 2 hours
|
|
22
|
+
mrctl tasks create --at "2026-03-02T16:00:00Z" --prompt "Time for the team meeting!"
|
|
23
|
+
|
|
24
|
+
# Schedule a future check
|
|
25
|
+
mrctl tasks create --at "2026-03-15T09:00:00Z" --prompt "Follow up on the Q1 report"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
At-tasks are useful for:
|
|
29
|
+
- **Reminders** — one-time notifications
|
|
30
|
+
- **Delayed actions** — schedule something for later
|
|
31
|
+
- **Follow-ups** — check back on something at a specific time
|
|
32
|
+
|
|
33
|
+
The `at` timestamp must be:
|
|
34
|
+
- ISO 8601 format (e.g., `2026-03-02T14:00:00Z`)
|
|
35
|
+
- In the future
|
|
36
|
+
|
|
37
|
+
## Silent Tasks
|
|
38
|
+
|
|
39
|
+
Tasks can be marked as **silent** to execute without posting results to the chat. This is useful for:
|
|
40
|
+
|
|
41
|
+
- **Maintenance tasks** — cleanup, archiving, or housekeeping
|
|
42
|
+
- **Health checks** — periodic monitoring without noise
|
|
43
|
+
- **Background updates** — knowledge base updates, data syncing
|
|
44
|
+
|
|
45
|
+
The task executes normally but no message is sent to the space.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Create a silent cron task
|
|
49
|
+
mrctl tasks create --cron "0 3 * * *" --prompt "Run nightly maintenance" --silent
|
|
50
|
+
|
|
51
|
+
# Create a silent at-task
|
|
52
|
+
mrctl tasks create --at "2026-03-02T03:00:00Z" --prompt "One-time cleanup" --silent
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## How It Works
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
TaskScheduler.start()
|
|
59
|
+
│
|
|
60
|
+
└─► Poll loop (every 5 seconds)
|
|
61
|
+
│
|
|
62
|
+
├─► Query DB for due tasks (active=1, next_run_at <= now)
|
|
63
|
+
│
|
|
64
|
+
├─► For each due task:
|
|
65
|
+
│ │
|
|
66
|
+
│ ├─► [Cron task]
|
|
67
|
+
│ │ ├─► Compute next run time from cron expression
|
|
68
|
+
│ │ ├─► Update next_run_at in DB
|
|
69
|
+
│ │ └─► Execute handler
|
|
70
|
+
│ │
|
|
71
|
+
│ └─► [At task]
|
|
72
|
+
│ ├─► Execute handler
|
|
73
|
+
│ └─► Delete task from DB
|
|
74
|
+
│
|
|
75
|
+
└─► Schedule next poll
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Tasks are processed sequentially within a poll cycle. Each task runs as if the `createdBy` user sent the prompt.
|
|
79
|
+
|
|
80
|
+
**At-task lifecycle:**
|
|
81
|
+
1. Created with a future timestamp
|
|
82
|
+
2. Waits until scheduled time
|
|
83
|
+
3. Executes once
|
|
84
|
+
4. Auto-deletes (regardless of success/failure)
|
|
85
|
+
|
|
86
|
+
## Creating Tasks
|
|
87
|
+
|
|
88
|
+
The agent creates tasks via `mrctl`:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# === Cron tasks (recurring) ===
|
|
92
|
+
|
|
93
|
+
# Daily standup at 9am
|
|
94
|
+
mrctl tasks create --cron "0 9 * * *" --prompt "Good morning! What's on the agenda today?"
|
|
95
|
+
|
|
96
|
+
# Weekly summary on Fridays at 5pm
|
|
97
|
+
mrctl tasks create --cron "0 17 * * 5" --prompt "Generate a summary of this week's discussions."
|
|
98
|
+
|
|
99
|
+
# Every 6 hours
|
|
100
|
+
mrctl tasks create --cron "0 */6 * * *" --prompt "Check for any pending items."
|
|
101
|
+
|
|
102
|
+
# Silent nightly cleanup (no chat output)
|
|
103
|
+
mrctl tasks create --cron "0 3 * * *" --prompt "Clean up old temp files" --silent
|
|
104
|
+
|
|
105
|
+
# === At tasks (one-shot) ===
|
|
106
|
+
|
|
107
|
+
# Reminder at a specific time
|
|
108
|
+
mrctl tasks create --at "2026-03-02T14:00:00Z" --prompt "Meeting starts in 15 minutes!"
|
|
109
|
+
|
|
110
|
+
# Delayed follow-up
|
|
111
|
+
mrctl tasks create --at "2026-03-10T09:00:00Z" --prompt "Check if the deployment completed successfully"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Note:** You must specify either `--cron` or `--at`, not both.
|
|
115
|
+
|
|
116
|
+
## Managing Tasks
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# List all tasks in the current space
|
|
120
|
+
mrctl tasks list
|
|
121
|
+
|
|
122
|
+
# Pause a task (stops execution, keeps definition)
|
|
123
|
+
mrctl tasks pause <id>
|
|
124
|
+
|
|
125
|
+
# Resume a paused task
|
|
126
|
+
mrctl tasks resume <id>
|
|
127
|
+
|
|
128
|
+
# Manually trigger a task now
|
|
129
|
+
mrctl tasks run <id>
|
|
130
|
+
|
|
131
|
+
# Delete a task permanently
|
|
132
|
+
mrctl tasks delete <id>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Cron Format
|
|
136
|
+
|
|
137
|
+
Standard 5-field cron expressions:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
┌───────────── minute (0-59)
|
|
141
|
+
│ ┌───────────── hour (0-23)
|
|
142
|
+
│ │ ┌───────────── day of month (1-31)
|
|
143
|
+
│ │ │ ┌───────────── month (1-12)
|
|
144
|
+
│ │ │ │ ┌───────────── day of week (0-7, 0 and 7 are Sunday)
|
|
145
|
+
│ │ │ │ │
|
|
146
|
+
* * * * *
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
|
|
151
|
+
| Expression | Description |
|
|
152
|
+
|------------|-------------|
|
|
153
|
+
| `0 9 * * *` | Every day at 9:00 AM |
|
|
154
|
+
| `0 9 * * 1-5` | Weekdays at 9:00 AM |
|
|
155
|
+
| `*/15 * * * *` | Every 15 minutes |
|
|
156
|
+
| `0 */6 * * *` | Every 6 hours |
|
|
157
|
+
| `0 17 * * 5` | Fridays at 5:00 PM |
|
|
158
|
+
| `0 0 1 * *` | First day of each month at midnight |
|
|
159
|
+
|
|
160
|
+
Mercury uses [cron-parser](https://www.npmjs.com/package/cron-parser) for parsing.
|
|
161
|
+
|
|
162
|
+
## Task Execution
|
|
163
|
+
|
|
164
|
+
When a task fires:
|
|
165
|
+
|
|
166
|
+
1. The prompt is sent to the space as if from the task creator
|
|
167
|
+
2. Runs through the normal routing (trigger check bypassed for scheduled tasks)
|
|
168
|
+
3. Caller ID is the `createdBy` user
|
|
169
|
+
4. Permissions are checked against the creator's role at execution time
|
|
170
|
+
|
|
171
|
+
Tasks run with `system` caller privileges for the routing layer, but the prompt is attributed to the original creator.
|
|
172
|
+
|
|
173
|
+
## Storage
|
|
174
|
+
|
|
175
|
+
Tasks are stored in SQLite:
|
|
176
|
+
|
|
177
|
+
```sql
|
|
178
|
+
CREATE TABLE tasks (
|
|
179
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
180
|
+
space_id TEXT NOT NULL,
|
|
181
|
+
cron TEXT, -- Cron expression (null for at-tasks)
|
|
182
|
+
at TEXT, -- ISO 8601 timestamp (null for cron-tasks)
|
|
183
|
+
prompt TEXT NOT NULL,
|
|
184
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
185
|
+
silent INTEGER NOT NULL DEFAULT 0,
|
|
186
|
+
next_run_at INTEGER NOT NULL,
|
|
187
|
+
created_by TEXT NOT NULL,
|
|
188
|
+
created_at INTEGER NOT NULL,
|
|
189
|
+
updated_at INTEGER NOT NULL
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
CREATE INDEX idx_tasks_next ON tasks(active, next_run_at);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
| Column | Description |
|
|
196
|
+
|--------|-------------|
|
|
197
|
+
| `cron` | Cron expression for recurring tasks (null for at-tasks) |
|
|
198
|
+
| `at` | ISO 8601 timestamp for one-shot tasks (null for cron-tasks) |
|
|
199
|
+
| `silent` | If 1, task runs but doesn't post results to chat |
|
|
200
|
+
|
|
201
|
+
## Permissions
|
|
202
|
+
|
|
203
|
+
Task management requires specific permissions:
|
|
204
|
+
|
|
205
|
+
| Permission | Action |
|
|
206
|
+
|------------|--------|
|
|
207
|
+
| `tasks.list` | View scheduled tasks |
|
|
208
|
+
| `tasks.create` | Create new tasks |
|
|
209
|
+
| `tasks.pause` | Pause a task |
|
|
210
|
+
| `tasks.resume` | Resume a paused task |
|
|
211
|
+
| `tasks.delete` | Delete a task |
|
|
212
|
+
|
|
213
|
+
By default, only `admin` has these permissions. Grant to other roles:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
mrctl permissions set member prompt,tasks.list
|
|
217
|
+
mrctl permissions set moderator prompt,tasks.list,tasks.pause,tasks.resume
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Lifecycle
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
mercury run
|
|
224
|
+
│
|
|
225
|
+
├─► runtime.initialize()
|
|
226
|
+
│
|
|
227
|
+
├─► scheduler.start(handler)
|
|
228
|
+
│ └─► Poll loop begins
|
|
229
|
+
│
|
|
230
|
+
├─► ... running ...
|
|
231
|
+
│
|
|
232
|
+
└─► SIGTERM/SIGINT
|
|
233
|
+
└─► scheduler.stop()
|
|
234
|
+
└─► Poll loop ends (graceful)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The scheduler stops cleanly on shutdown — no orphaned timers.
|
|
238
|
+
|
|
239
|
+
## API
|
|
240
|
+
|
|
241
|
+
### `TaskScheduler`
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const scheduler = new TaskScheduler(db, pollIntervalMs);
|
|
245
|
+
|
|
246
|
+
scheduler.start(handler); // Begin polling
|
|
247
|
+
scheduler.stop(); // Stop polling
|
|
248
|
+
scheduler.computeNextRun(cron, from); // Get next run time for cron tasks
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Handler Signature
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
type TaskHandler = (task: {
|
|
255
|
+
id: number;
|
|
256
|
+
spaceId: string;
|
|
257
|
+
prompt: string;
|
|
258
|
+
createdBy: string;
|
|
259
|
+
silent: boolean;
|
|
260
|
+
}) => Promise<void>;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Database Methods
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// Create a cron task
|
|
267
|
+
db.createTask(spaceId, { cron: "0 9 * * *" }, prompt, nextRunAt, createdBy, silent);
|
|
268
|
+
|
|
269
|
+
// Create an at-task
|
|
270
|
+
db.createTask(spaceId, { at: "2026-03-02T14:00:00Z" }, prompt, nextRunAt, createdBy, silent);
|
|
271
|
+
|
|
272
|
+
db.listTasks(spaceId?); // List tasks (optionally filter by space)
|
|
273
|
+
db.getDueTasks(now); // Get tasks ready to run
|
|
274
|
+
db.getTask(id); // Get single task
|
|
275
|
+
db.setTaskActive(id, active); // Pause/resume
|
|
276
|
+
db.deleteTask(id, spaceId); // Delete task (with space check)
|
|
277
|
+
db.deleteTaskById(id); // Delete task (no space check, for scheduler)
|
|
278
|
+
db.updateTaskNextRun(id, nextRunAt); // Update next execution time
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Error Handling
|
|
282
|
+
|
|
283
|
+
If a task handler fails:
|
|
284
|
+
- Error is logged
|
|
285
|
+
- Task is not retried in the same cycle
|
|
286
|
+
- **Cron tasks:** `next_run_at` is already updated, so it will run again at the next scheduled time
|
|
287
|
+
- **At tasks:** Still deleted after execution (one-shot behavior preserved)
|
|
288
|
+
- Other tasks in the cycle continue to execute
|