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.
Files changed (125) hide show
  1. package/README.md +452 -451
  2. package/container/Dockerfile +127 -127
  3. package/container/Dockerfile.base +109 -109
  4. package/container/Dockerfile.power +17 -17
  5. package/container/agent-package.json +8 -8
  6. package/container/build.sh +54 -54
  7. package/docs/ROADMAP.md +3 -4
  8. package/docs/archive/summarization/2026-07-02-recent-dev-summary.md +71 -0
  9. package/docs/auth/dashboard.md +28 -28
  10. package/docs/auth/overview.md +109 -109
  11. package/docs/auth/whatsapp.md +173 -173
  12. package/docs/authoring-profiles.md +174 -174
  13. package/docs/configuration.md +54 -54
  14. package/docs/container-lifecycle.md +349 -349
  15. package/docs/{bugs/bwrap-privileged-linux-docker.md → debug/major/2026-07-02-bwrap-privileged-linux-docker.md} +21 -1
  16. package/docs/{bugs/e2big-prompt-delivery-fix.md → debug/major/2026-07-02-e2big-prompt-delivery-fix.md} +27 -1
  17. package/docs/{bugs/registry-pull-no-local-fallback.md → debug/major/2026-07-02-registry-pull-no-local-fallback.md} +27 -1
  18. package/docs/debug/summarization/2026-07-02-common-bug-patterns.md +72 -0
  19. package/docs/deployment.md +199 -199
  20. package/docs/extensions.md +375 -375
  21. package/docs/graceful-shutdown.md +62 -62
  22. package/docs/kb-distillation.md +77 -77
  23. package/docs/media/overview.md +140 -140
  24. package/docs/media/whatsapp.md +171 -171
  25. package/docs/memory.md +137 -137
  26. package/docs/permissions.md +217 -217
  27. package/docs/pipeline.md +228 -228
  28. package/docs/prd-chat-memory.md +76 -76
  29. package/docs/prd-config-load.md +82 -82
  30. package/docs/rate-limiting.md +229 -229
  31. package/docs/runbooks/publish-checklist.md +9 -1
  32. package/docs/scheduler.md +288 -288
  33. package/docs/setup-discord.md +100 -100
  34. package/docs/setup-slack.md +119 -119
  35. package/docs/setup-whatsapp.md +94 -94
  36. package/docs/subagents.md +166 -166
  37. package/docs/web-search.md +62 -62
  38. package/examples/extensions/README.md +12 -12
  39. package/examples/extensions/charts/index.ts +13 -13
  40. package/examples/extensions/charts/skill/SKILL.md +98 -98
  41. package/examples/extensions/gws/README.md +52 -52
  42. package/examples/extensions/gws/skill/SKILL.md +57 -57
  43. package/examples/extensions/gws/skill/references/calendar.md +101 -101
  44. package/examples/extensions/gws/skill/references/docs.md +65 -65
  45. package/examples/extensions/gws/skill/references/drive.md +79 -79
  46. package/examples/extensions/gws/skill/references/gmail.md +85 -85
  47. package/examples/extensions/gws/skill/references/sheets.md +60 -60
  48. package/examples/extensions/napkin/skill/SKILL.md +728 -728
  49. package/examples/extensions/pdf/skill/LICENSE.txt +30 -30
  50. package/examples/extensions/pdf/skill/SKILL.md +314 -314
  51. package/examples/extensions/pdf/skill/forms.md +294 -294
  52. package/examples/extensions/pdf/skill/reference.md +611 -611
  53. package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -65
  54. package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -11
  55. package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -33
  56. package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -37
  57. package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -122
  58. package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -115
  59. package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -98
  60. package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -107
  61. package/examples/extensions/permission-guard/index.ts +65 -65
  62. package/examples/extensions/pinchtab/skill/SKILL.md +224 -224
  63. package/examples/extensions/pinchtab/skill/TRUST.md +69 -69
  64. package/examples/extensions/pinchtab/skill/references/api.md +297 -297
  65. package/examples/extensions/pinchtab/skill/references/env.md +45 -45
  66. package/examples/extensions/pinchtab/skill/references/profiles.md +107 -107
  67. package/examples/extensions/tradestation/host/refresh.ts +102 -102
  68. package/examples/extensions/tradestation/index.ts +153 -153
  69. package/examples/extensions/tradestation/skill/SKILL.md +67 -67
  70. package/examples/extensions/voice-synth/index.ts +94 -94
  71. package/examples/extensions/voice-synth/skill/SKILL.md +38 -38
  72. package/examples/extensions/voice-transcribe/requirements.txt +8 -8
  73. package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -179
  74. package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -53
  75. package/examples/extensions/yahoo-mail/cli/package.json +13 -13
  76. package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -78
  77. package/package.json +106 -106
  78. package/resources/agents/explore.md +50 -50
  79. package/resources/agents/worker.md +24 -24
  80. package/resources/connection-env-vars.json +25 -25
  81. package/resources/pi-extensions/subagent/agents.ts +126 -126
  82. package/resources/pi-extensions/subagent/index.ts +964 -964
  83. package/resources/profiles/coding/AGENTS.md +43 -43
  84. package/resources/profiles/coding/mercury-profile.yaml +15 -15
  85. package/resources/profiles/general/AGENTS.md +31 -31
  86. package/resources/profiles/general/mercury-profile.yaml +15 -15
  87. package/resources/profiles/research/AGENTS.md +40 -40
  88. package/resources/profiles/research/mercury-profile.yaml +15 -15
  89. package/resources/skills/config/SKILL.md +25 -25
  90. package/resources/skills/context/SKILL.md +33 -33
  91. package/resources/skills/conversation-recap/SKILL.md +19 -19
  92. package/resources/skills/mutes/SKILL.md +31 -31
  93. package/resources/skills/permissions/SKILL.md +19 -19
  94. package/resources/skills/preferences/SKILL.md +31 -31
  95. package/resources/skills/recall/SKILL.md +24 -24
  96. package/resources/skills/roles/SKILL.md +18 -18
  97. package/resources/skills/spaces/SKILL.md +18 -18
  98. package/resources/skills/tasks/SKILL.md +45 -45
  99. package/resources/templates/AGENTS.md +157 -157
  100. package/resources/templates/env.template +38 -38
  101. package/resources/templates/mercury.example.yaml +99 -99
  102. package/src/agent/container-entry.ts +1 -1
  103. package/src/agent/container-runner.ts +1345 -1346
  104. package/src/cli/mercury.ts +39 -7
  105. package/src/cli/mrctl.ts +636 -636
  106. package/src/config-file.ts +540 -540
  107. package/src/config.ts +339 -339
  108. package/src/core/api.ts +125 -125
  109. package/src/core/caller-token.ts +101 -101
  110. package/src/core/permissions.ts +228 -228
  111. package/src/core/profiles.ts +271 -271
  112. package/src/core/routes/capability.ts +70 -70
  113. package/src/core/routes/index.ts +15 -15
  114. package/src/core/runtime.ts +1530 -1530
  115. package/src/dashboard/index.html +729 -729
  116. package/src/extensions/api.ts +273 -273
  117. package/src/extensions/loader.ts +286 -286
  118. package/src/extensions/types.ts +517 -517
  119. package/src/main.ts +605 -605
  120. package/docs/pending-updates/applicative-profiles.md +0 -15
  121. package/docs/pending-updates/caller-bound-capability-token.md +0 -15
  122. package/docs/pending-updates/dm-auto-space.md +0 -15
  123. /package/docs/archive/{2026-07-01-applicative-profiles.md → applicative-profiles/2026-07-01-applicative-profiles.md} +0 -0
  124. /package/docs/archive/{2026-07-01-caller-bound-capability-token.md → applicative-profiles/2026-07-01-caller-bound-capability-token.md} +0 -0
  125. /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