mercury-agent 0.4.5
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/LICENSE +22 -0
- package/README.md +438 -0
- package/container/Dockerfile +127 -0
- package/container/Dockerfile.base +109 -0
- package/container/Dockerfile.power +17 -0
- package/container/agent-package.json +8 -0
- package/container/build.sh +54 -0
- package/docs/TODOS.md +147 -0
- package/docs/auth/dashboard.md +28 -0
- package/docs/auth/overview.md +109 -0
- package/docs/auth/whatsapp.md +173 -0
- package/docs/configuration.md +54 -0
- package/docs/container-lifecycle.md +349 -0
- package/docs/context-architecture.md +87 -0
- package/docs/deployment.md +199 -0
- package/docs/extensions.md +375 -0
- package/docs/graceful-shutdown.md +62 -0
- package/docs/kb-distillation.md +77 -0
- package/docs/media/overview.md +140 -0
- package/docs/media/whatsapp.md +171 -0
- package/docs/memory.md +137 -0
- package/docs/permissions.md +217 -0
- package/docs/pipeline.md +228 -0
- package/docs/prd-chat-memory.md +76 -0
- package/docs/prd-config-load.md +82 -0
- package/docs/rate-limiting.md +166 -0
- package/docs/scheduler.md +288 -0
- package/docs/setup-discord.md +100 -0
- package/docs/setup-slack.md +119 -0
- package/docs/setup-whatsapp.md +94 -0
- package/docs/subagents.md +166 -0
- package/docs/web-search.md +62 -0
- package/examples/extensions/README.md +12 -0
- package/examples/extensions/charts/index.ts +13 -0
- package/examples/extensions/charts/skill/SKILL.md +98 -0
- package/examples/extensions/gws/README.md +52 -0
- package/examples/extensions/gws/index.ts +106 -0
- package/examples/extensions/gws/skill/SKILL.md +57 -0
- package/examples/extensions/gws/skill/references/calendar.md +101 -0
- package/examples/extensions/gws/skill/references/docs.md +65 -0
- package/examples/extensions/gws/skill/references/drive.md +79 -0
- package/examples/extensions/gws/skill/references/gmail.md +85 -0
- package/examples/extensions/gws/skill/references/sheets.md +60 -0
- package/examples/extensions/napkin/index.ts +821 -0
- package/examples/extensions/napkin/prompts/consolidation-monthly.md +73 -0
- package/examples/extensions/napkin/prompts/consolidation-weekly.md +67 -0
- package/examples/extensions/napkin/prompts/kb-distillation.md +176 -0
- package/examples/extensions/napkin/skill/SKILL.md +728 -0
- package/examples/extensions/pdf/index.ts +23 -0
- package/examples/extensions/pdf/skill/LICENSE.txt +30 -0
- package/examples/extensions/pdf/skill/SKILL.md +314 -0
- package/examples/extensions/pdf/skill/forms.md +294 -0
- package/examples/extensions/pdf/skill/reference.md +612 -0
- package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -0
- package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -0
- package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -0
- package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -0
- package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -0
- package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -0
- package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -0
- package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/examples/extensions/permission-guard/index.ts +65 -0
- package/examples/extensions/pinchtab/index.ts +199 -0
- package/examples/extensions/pinchtab/lib/session-injector.ts +144 -0
- package/examples/extensions/pinchtab/skill/SKILL.md +224 -0
- package/examples/extensions/pinchtab/skill/TRUST.md +69 -0
- package/examples/extensions/pinchtab/skill/references/api.md +297 -0
- package/examples/extensions/pinchtab/skill/references/env.md +45 -0
- package/examples/extensions/pinchtab/skill/references/profiles.md +107 -0
- package/examples/extensions/tradestation/host/refresh.ts +102 -0
- package/examples/extensions/tradestation/index.ts +153 -0
- package/examples/extensions/tradestation/skill/SKILL.md +67 -0
- package/examples/extensions/tradestation/skill/scripts/ts-cli.ts +111 -0
- package/examples/extensions/voice-synth/index.ts +94 -0
- package/examples/extensions/voice-synth/skill/SKILL.md +38 -0
- package/examples/extensions/voice-transcribe/index.ts +381 -0
- package/examples/extensions/voice-transcribe/requirements.txt +8 -0
- package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -0
- package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -0
- package/examples/extensions/web-search/index.ts +22 -0
- package/examples/extensions/web-search/skill/SKILL.md +114 -0
- package/examples/extensions/web-search/skill/references/apartments.md +178 -0
- package/examples/extensions/web-search/skill/references/car-purchase.md +132 -0
- package/examples/extensions/web-search/skill/references/car-rental.md +113 -0
- package/examples/extensions/web-search/skill/references/flights.md +133 -0
- package/examples/extensions/web-search/skill/references/hotels.md +148 -0
- package/examples/extensions/yahoo-mail/cli/bun.lock +66 -0
- package/examples/extensions/yahoo-mail/cli/package.json +13 -0
- package/examples/extensions/yahoo-mail/cli/ymail.mjs +353 -0
- package/examples/extensions/yahoo-mail/index.ts +57 -0
- package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -0
- package/package.json +106 -0
- package/resources/agents/explore.md +50 -0
- package/resources/agents/worker.md +24 -0
- package/resources/builtin-extensions.txt +3 -0
- package/resources/connection-env-vars.json +25 -0
- package/resources/extensions/.gitkeep +0 -0
- package/resources/pi-extensions/subagent/agents.ts +126 -0
- package/resources/pi-extensions/subagent/index.ts +964 -0
- package/resources/profiles/coding/AGENTS.md +43 -0
- package/resources/profiles/coding/mercury-profile.yaml +15 -0
- package/resources/profiles/general/AGENTS.md +31 -0
- package/resources/profiles/general/mercury-profile.yaml +15 -0
- package/resources/profiles/research/AGENTS.md +40 -0
- package/resources/profiles/research/mercury-profile.yaml +15 -0
- package/resources/skills/config/SKILL.md +25 -0
- package/resources/skills/context/SKILL.md +33 -0
- package/resources/skills/conversation-recap/SKILL.md +19 -0
- package/resources/skills/media/SKILL.md +27 -0
- package/resources/skills/mutes/SKILL.md +31 -0
- package/resources/skills/permissions/SKILL.md +19 -0
- package/resources/skills/preferences/SKILL.md +31 -0
- package/resources/skills/recall/SKILL.md +24 -0
- package/resources/skills/roles/SKILL.md +18 -0
- package/resources/skills/spaces/SKILL.md +18 -0
- package/resources/skills/tasks/SKILL.md +45 -0
- package/resources/templates/AGENTS.md +157 -0
- package/resources/templates/env.template +34 -0
- package/resources/templates/mercury.example.yaml +75 -0
- package/src/adapters/discord-native.ts +534 -0
- package/src/adapters/discord.ts +38 -0
- package/src/adapters/setup.ts +89 -0
- package/src/adapters/slack.ts +9 -0
- package/src/adapters/whatsapp-media.ts +337 -0
- package/src/adapters/whatsapp.ts +629 -0
- package/src/agent/api-socket.ts +127 -0
- package/src/agent/container-entry.ts +967 -0
- package/src/agent/container-error.ts +49 -0
- package/src/agent/container-runner.ts +1272 -0
- package/src/agent/model-capabilities-core.ts +23 -0
- package/src/agent/model-capabilities.ts +231 -0
- package/src/agent/pi-failure-class.ts +83 -0
- package/src/agent/pi-jsonl-parser.ts +306 -0
- package/src/agent/preferences-prompt.ts +20 -0
- package/src/agent/user-error-messages.ts +78 -0
- package/src/bridges/discord.ts +171 -0
- package/src/bridges/slack.ts +177 -0
- package/src/bridges/teams.ts +160 -0
- package/src/bridges/telegram.ts +571 -0
- package/src/bridges/whatsapp.ts +290 -0
- package/src/chat-shim.ts +259 -0
- package/src/cli/mercury.ts +2508 -0
- package/src/cli/mrctl-http.ts +27 -0
- package/src/cli/mrctl.ts +611 -0
- package/src/cli/whatsapp-auth.ts +260 -0
- package/src/config-file.ts +397 -0
- package/src/config-model-chain.ts +30 -0
- package/src/config.ts +316 -0
- package/src/core/api-types.ts +58 -0
- package/src/core/api.ts +105 -0
- package/src/core/commands.ts +76 -0
- package/src/core/conversation.ts +47 -0
- package/src/core/handler.ts +206 -0
- package/src/core/media.ts +200 -0
- package/src/core/mute-duration.ts +22 -0
- package/src/core/outbox.ts +76 -0
- package/src/core/permissions.ts +192 -0
- package/src/core/profiles.ts +245 -0
- package/src/core/rate-limiter.ts +127 -0
- package/src/core/router.ts +191 -0
- package/src/core/routes/chat.ts +172 -0
- package/src/core/routes/config-builtin.ts +107 -0
- package/src/core/routes/config.ts +81 -0
- package/src/core/routes/connections.ts +190 -0
- package/src/core/routes/console.ts +668 -0
- package/src/core/routes/control.ts +46 -0
- package/src/core/routes/conversations.ts +66 -0
- package/src/core/routes/dashboard.ts +2491 -0
- package/src/core/routes/extensions.ts +37 -0
- package/src/core/routes/index.ts +14 -0
- package/src/core/routes/media.ts +72 -0
- package/src/core/routes/messages.ts +37 -0
- package/src/core/routes/mutes.ts +89 -0
- package/src/core/routes/prefs.ts +95 -0
- package/src/core/routes/roles.ts +125 -0
- package/src/core/routes/spaces.ts +60 -0
- package/src/core/routes/storage.ts +126 -0
- package/src/core/routes/tasks.ts +189 -0
- package/src/core/routes/tradestation.ts +268 -0
- package/src/core/routes/tts.ts +51 -0
- package/src/core/runtime.ts +1140 -0
- package/src/core/space-queue.ts +103 -0
- package/src/core/storage-cleanup.ts +140 -0
- package/src/core/storage-guard.ts +24 -0
- package/src/core/task-scheduler.ts +132 -0
- package/src/core/telegram-format.ts +178 -0
- package/src/core/trigger.ts +142 -0
- package/src/dashboard/index.html +729 -0
- package/src/dashboard/tokens.css +53 -0
- package/src/extensions/api.ts +252 -0
- package/src/extensions/catalog.ts +117 -0
- package/src/extensions/config-registry.ts +83 -0
- package/src/extensions/context.ts +36 -0
- package/src/extensions/hooks.ts +156 -0
- package/src/extensions/image-builder.ts +617 -0
- package/src/extensions/installer.ts +306 -0
- package/src/extensions/jobs.ts +122 -0
- package/src/extensions/loader.ts +271 -0
- package/src/extensions/permission-guard.ts +52 -0
- package/src/extensions/reserved.ts +28 -0
- package/src/extensions/skills.ts +123 -0
- package/src/extensions/types.ts +462 -0
- package/src/logger.ts +174 -0
- package/src/main.ts +586 -0
- package/src/server.ts +391 -0
- package/src/storage/db.ts +1624 -0
- package/src/storage/memory.ts +45 -0
- package/src/storage/pi-auth.ts +95 -0
- package/src/text/markdown.ts +117 -0
- package/src/text/rtl.ts +38 -0
- package/src/tradestation/host-api.ts +77 -0
- package/src/tradestation/pending-orders.ts +69 -0
- package/src/tts/azure.ts +52 -0
- package/src/tts/google.ts +128 -0
- package/src/tts/index.ts +8 -0
- package/src/tts/language.ts +20 -0
- package/src/tts/synthesize.ts +133 -0
- package/src/types.ts +295 -0
|
@@ -0,0 +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
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Discord Setup Guide
|
|
2
|
+
|
|
3
|
+
Connect Mercury to Discord using a bot application with gateway (WebSocket) connection.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- A Discord account
|
|
8
|
+
- A Discord server where you have **Manage Server** permission
|
|
9
|
+
- Mercury initialized (`mercury init`)
|
|
10
|
+
|
|
11
|
+
## Step 1: Create a Discord Application
|
|
12
|
+
|
|
13
|
+
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
|
|
14
|
+
2. Click **New Application** → name it (e.g., "Mercury")
|
|
15
|
+
3. Go to the **Bot** tab
|
|
16
|
+
4. Click **Reset Token** → copy the bot token
|
|
17
|
+
|
|
18
|
+
## Step 2: Configure Bot Settings
|
|
19
|
+
|
|
20
|
+
In the **Bot** tab:
|
|
21
|
+
|
|
22
|
+
- **Privileged Gateway Intents** — enable all three:
|
|
23
|
+
- ✅ Presence Intent
|
|
24
|
+
- ✅ Server Members Intent
|
|
25
|
+
- ✅ Message Content Intent
|
|
26
|
+
|
|
27
|
+
These are required for Mercury to read message content and user information.
|
|
28
|
+
|
|
29
|
+
## Step 3: Invite the Bot to Your Server
|
|
30
|
+
|
|
31
|
+
1. Go to the **OAuth2 → URL Generator** tab
|
|
32
|
+
2. Select scopes: `bot`
|
|
33
|
+
3. Select bot permissions:
|
|
34
|
+
- Send Messages
|
|
35
|
+
- Read Message History
|
|
36
|
+
- Attach Files
|
|
37
|
+
- Use Slash Commands
|
|
38
|
+
- Add Reactions
|
|
39
|
+
4. Copy the generated URL and open it in your browser
|
|
40
|
+
5. Select your server and authorize
|
|
41
|
+
|
|
42
|
+
## Step 4: Configure Mercury
|
|
43
|
+
|
|
44
|
+
In your `.env` file:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
MERCURY_ENABLE_DISCORD=true
|
|
48
|
+
MERCURY_DISCORD_BOT_TOKEN=your-bot-token-here
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Step 5: Find Your Discord User ID
|
|
52
|
+
|
|
53
|
+
To add yourself as admin, you need your Discord user ID:
|
|
54
|
+
|
|
55
|
+
1. Enable **Developer Mode** in Discord (Settings → Advanced → Developer Mode)
|
|
56
|
+
2. Right-click your username → **Copy User ID**
|
|
57
|
+
3. Add to `.env`:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
MERCURY_ADMINS=discord:YOUR_USER_ID
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Step 6: Start Mercury
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mercury service install
|
|
67
|
+
mercury service status
|
|
68
|
+
mercury service logs -f
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Step 7: Link conversations
|
|
72
|
+
|
|
73
|
+
Send a message to the bot (DM or mention in a channel), then:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
mercury conversations --unlinked
|
|
77
|
+
mercury link <id> <space-name>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Environment Variables
|
|
81
|
+
|
|
82
|
+
| Variable | Default | Description |
|
|
83
|
+
|----------|---------|-------------|
|
|
84
|
+
| `MERCURY_ENABLE_DISCORD` | `false` | Enable Discord adapter |
|
|
85
|
+
| `MERCURY_DISCORD_BOT_TOKEN` | — | Bot token from Developer Portal |
|
|
86
|
+
|
|
87
|
+
## Troubleshooting
|
|
88
|
+
|
|
89
|
+
| Problem | Solution |
|
|
90
|
+
|---------|----------|
|
|
91
|
+
| Bot is online but doesn't respond | Check Message Content Intent is enabled |
|
|
92
|
+
| "Missing Permissions" errors | Re-invite with correct permissions (step 3) |
|
|
93
|
+
| Bot doesn't appear online | Verify `MERCURY_DISCORD_BOT_TOKEN` is correct |
|
|
94
|
+
| Messages not arriving in logs | Ensure bot is in the channel and has Read Message History |
|
|
95
|
+
|
|
96
|
+
## Security
|
|
97
|
+
|
|
98
|
+
- Never commit your bot token to version control
|
|
99
|
+
- Regenerate the token immediately if it's ever exposed
|
|
100
|
+
- Use minimal permissions — only grant what Mercury needs
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Slack Setup Guide
|
|
2
|
+
|
|
3
|
+
Connect Mercury to Slack using the Events API with a bot token.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- A Slack workspace where you have admin access
|
|
8
|
+
- A publicly accessible URL for webhooks (or use a tunnel like ngrok)
|
|
9
|
+
- Mercury initialized (`mercury init`)
|
|
10
|
+
|
|
11
|
+
## Step 1: Create a Slack App
|
|
12
|
+
|
|
13
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps)
|
|
14
|
+
2. Click **Create New App → From scratch**
|
|
15
|
+
3. Name it (e.g., "Mercury") and select your workspace
|
|
16
|
+
|
|
17
|
+
## Step 2: Configure Bot Scopes
|
|
18
|
+
|
|
19
|
+
Go to **OAuth & Permissions** → **Bot Token Scopes** and add:
|
|
20
|
+
|
|
21
|
+
- `chat:write` — Send messages
|
|
22
|
+
- `channels:history` — Read channel messages
|
|
23
|
+
- `groups:history` — Read private channel messages
|
|
24
|
+
- `im:history` — Read DMs
|
|
25
|
+
- `mpim:history` — Read group DMs
|
|
26
|
+
- `channels:read` — List channels
|
|
27
|
+
- `groups:read` — List private channels
|
|
28
|
+
- `im:read` — List DMs
|
|
29
|
+
- `users:read` — Read user info
|
|
30
|
+
- `files:read` — Access shared files
|
|
31
|
+
- `files:write` — Upload files
|
|
32
|
+
|
|
33
|
+
## Step 3: Install to Workspace
|
|
34
|
+
|
|
35
|
+
1. Go to **OAuth & Permissions**
|
|
36
|
+
2. Click **Install to Workspace** → authorize
|
|
37
|
+
3. Copy the **Bot User OAuth Token** (`xoxb-...`)
|
|
38
|
+
|
|
39
|
+
## Step 4: Get the Signing Secret
|
|
40
|
+
|
|
41
|
+
1. Go to **Basic Information**
|
|
42
|
+
2. Under **App Credentials**, copy the **Signing Secret**
|
|
43
|
+
|
|
44
|
+
## Step 5: Configure Mercury
|
|
45
|
+
|
|
46
|
+
In your `.env` file:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
MERCURY_ENABLE_SLACK=true
|
|
50
|
+
MERCURY_SLACK_BOT_TOKEN=xoxb-your-bot-token
|
|
51
|
+
MERCURY_SLACK_SIGNING_SECRET=your-signing-secret
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Step 6: Set Up Event Subscriptions
|
|
55
|
+
|
|
56
|
+
Mercury needs to receive events from Slack via webhooks.
|
|
57
|
+
|
|
58
|
+
1. Start Mercury first (it needs to respond to Slack's verification challenge):
|
|
59
|
+
```bash
|
|
60
|
+
mercury service install
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
2. In the Slack app settings, go to **Event Subscriptions**
|
|
64
|
+
3. Toggle **Enable Events** to on
|
|
65
|
+
4. Set the **Request URL** to: `https://your-domain.com/webhooks/slack/events`
|
|
66
|
+
5. Wait for Slack to verify the URL (Mercury handles this automatically)
|
|
67
|
+
|
|
68
|
+
6. Under **Subscribe to Bot Events**, add:
|
|
69
|
+
- `message.channels`
|
|
70
|
+
- `message.groups`
|
|
71
|
+
- `message.im`
|
|
72
|
+
- `message.mpim`
|
|
73
|
+
|
|
74
|
+
7. Click **Save Changes**
|
|
75
|
+
|
|
76
|
+
## Step 7: Find Your Slack User ID
|
|
77
|
+
|
|
78
|
+
To add yourself as admin:
|
|
79
|
+
|
|
80
|
+
1. In Slack, click your profile picture → **Profile**
|
|
81
|
+
2. Click **⋯** (more) → **Copy member ID**
|
|
82
|
+
3. Add to `.env`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
MERCURY_ADMINS=slack:U0123456789
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Step 8: Link conversations
|
|
89
|
+
|
|
90
|
+
Send a message to the bot (DM or mention in a channel), then:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
mercury conversations --unlinked
|
|
94
|
+
mercury link <id> <space-name>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Environment Variables
|
|
98
|
+
|
|
99
|
+
| Variable | Default | Description |
|
|
100
|
+
|----------|---------|-------------|
|
|
101
|
+
| `MERCURY_ENABLE_SLACK` | `false` | Enable Slack adapter |
|
|
102
|
+
| `MERCURY_SLACK_BOT_TOKEN` | — | Bot User OAuth Token (`xoxb-...`) |
|
|
103
|
+
| `MERCURY_SLACK_SIGNING_SECRET` | — | Signing secret for request verification |
|
|
104
|
+
|
|
105
|
+
## Troubleshooting
|
|
106
|
+
|
|
107
|
+
| Problem | Solution |
|
|
108
|
+
|---------|----------|
|
|
109
|
+
| Event URL verification fails | Ensure Mercury is running and reachable at the URL |
|
|
110
|
+
| Bot doesn't respond in channels | Invite the bot to the channel (`/invite @Mercury`) |
|
|
111
|
+
| "not_authed" errors | Check `MERCURY_SLACK_BOT_TOKEN` is correct |
|
|
112
|
+
| Missing messages | Verify all `message.*` events are subscribed |
|
|
113
|
+
| Can't receive events locally | Use ngrok: `ngrok http 8787` and use the HTTPS URL |
|
|
114
|
+
|
|
115
|
+
## Security
|
|
116
|
+
|
|
117
|
+
- Never commit tokens or signing secrets to version control
|
|
118
|
+
- The signing secret verifies that events come from Slack — keep it secret
|
|
119
|
+
- Rotate tokens if exposed: **OAuth & Permissions → Revoke Token**
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# WhatsApp Setup Guide
|
|
2
|
+
|
|
3
|
+
Connect Mercury to WhatsApp using the Baileys library (WhatsApp Web protocol).
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- A phone number with WhatsApp installed
|
|
8
|
+
- Mercury initialized (`mercury init`)
|
|
9
|
+
|
|
10
|
+
## Step 1: Enable WhatsApp
|
|
11
|
+
|
|
12
|
+
In your `.env` file:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
MERCURY_ENABLE_WHATSAPP=true
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Step 2: Authenticate
|
|
19
|
+
|
|
20
|
+
You **must** authenticate before starting Mercury.
|
|
21
|
+
|
|
22
|
+
### QR Code (recommended)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
mercury auth whatsapp
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
1. Open WhatsApp on your phone
|
|
29
|
+
2. Go to **Settings → Linked Devices → Link a Device**
|
|
30
|
+
3. Scan the QR code displayed in your terminal
|
|
31
|
+
|
|
32
|
+
### Pairing Code (headless/remote servers)
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
mercury auth whatsapp --pairing-code --phone 14155551234
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
1. Open WhatsApp → **Settings → Linked Devices → Link a Device**
|
|
39
|
+
2. Tap **"Link with phone number instead"**
|
|
40
|
+
3. Enter the 8-character code shown in your terminal
|
|
41
|
+
|
|
42
|
+
After successful auth, your WhatsApp ID is printed — copy it into `MERCURY_ADMINS` in `.env`.
|
|
43
|
+
|
|
44
|
+
## Step 3: Start Mercury
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
mercury service install
|
|
48
|
+
mercury service status
|
|
49
|
+
mercury service logs -f
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Step 4: Link conversations
|
|
53
|
+
|
|
54
|
+
Send a message to the bot's WhatsApp number, then:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
mercury conversations --unlinked
|
|
58
|
+
mercury link <id> <space-name>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Environment Variables
|
|
62
|
+
|
|
63
|
+
| Variable | Default | Description |
|
|
64
|
+
|----------|---------|-------------|
|
|
65
|
+
| `MERCURY_ENABLE_WHATSAPP` | `false` | Enable WhatsApp adapter |
|
|
66
|
+
| `MERCURY_WHATSAPP_AUTH_DIR` | `.mercury/whatsapp-auth` | Credentials directory |
|
|
67
|
+
|
|
68
|
+
## Session Lifecycle
|
|
69
|
+
|
|
70
|
+
WhatsApp linked device sessions last **~14–20 days** before requiring re-authentication. When expired:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
mercury service uninstall
|
|
74
|
+
rm -rf .mercury/whatsapp-auth/
|
|
75
|
+
mercury auth whatsapp
|
|
76
|
+
mercury service install
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Troubleshooting
|
|
80
|
+
|
|
81
|
+
| Problem | Solution |
|
|
82
|
+
|---------|----------|
|
|
83
|
+
| QR code not showing | Run `mercury auth whatsapp` separately, not `mercury run` |
|
|
84
|
+
| "Already authenticated" but not working | Delete `.mercury/whatsapp-auth/` and re-auth |
|
|
85
|
+
| QR code expires too fast | Use `--pairing-code` mode instead |
|
|
86
|
+
| Messages not arriving | Check `MERCURY_ENABLE_WHATSAPP=true` and re-auth |
|
|
87
|
+
| Old messages appear on startup | Normal — Mercury ignores pre-connection messages |
|
|
88
|
+
|
|
89
|
+
## Security
|
|
90
|
+
|
|
91
|
+
- Credentials in `.mercury/whatsapp-auth/` are sensitive — treat like passwords
|
|
92
|
+
- Consider using a dedicated phone number for the bot
|
|
93
|
+
|
|
94
|
+
See also: [auth/whatsapp.md](auth/whatsapp.md) for detailed auth internals.
|