mercury-agent 0.5.7 → 0.5.10
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 +29 -3
- package/container/Dockerfile +9 -5
- package/container/Dockerfile.base +2 -2
- package/container/build.sh +7 -38
- package/docs/authoring-profiles.md +2 -2
- package/docs/container-lifecycle.md +7 -11
- package/docs/memory.md +0 -6
- package/docs/prd-config-load.md +1 -1
- package/examples/extensions/napkin/index.ts +17 -10
- package/examples/extensions/napkin/prompts/kb-distillation.md +6 -6
- package/examples/extensions/pdf/skill/SKILL.md +545 -178
- package/examples/extensions/pdf/skill/forms.md +273 -214
- package/examples/extensions/pdf/skill/reference.md +450 -480
- package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +221 -58
- package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +90 -5
- package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +138 -22
- package/examples/extensions/pdf/skill/scripts/create_validation_image.py +191 -27
- package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +149 -104
- package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +215 -91
- package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +130 -86
- package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +204 -87
- package/examples/extensions/tradestation/skill/SKILL.md +2 -2
- package/package.json +17 -1
- package/resources/templates/env.template +0 -3
- package/src/adapters/whatsapp-media.ts +5 -2
- package/src/agent/container-entry.ts +2 -2
- package/src/agent/container-runner.ts +19 -14
- package/src/bridges/telegram.ts +5 -2
- package/src/chat-shim.ts +26 -17
- package/src/core/media.ts +5 -2
- package/src/dashboard/tokens.css +1 -1
- package/src/extensions/image-builder.ts +2 -2
- package/src/main.ts +2 -2
- package/examples/extensions/pdf/skill/LICENSE.txt +0 -30
package/README.md
CHANGED
|
@@ -52,21 +52,23 @@ Configure identity and adapters in `.env`:
|
|
|
52
52
|
MERCURY_BOT_USERNAME=Mercury
|
|
53
53
|
MERCURY_TRIGGER_PATTERNS=@Mercury,Mercury
|
|
54
54
|
|
|
55
|
-
# Enable adapters
|
|
55
|
+
# Enable adapters (pick one or more)
|
|
56
56
|
MERCURY_ENABLE_WHATSAPP=true
|
|
57
57
|
MERCURY_ENABLE_DISCORD=true
|
|
58
58
|
MERCURY_DISCORD_BOT_TOKEN=your-bot-token
|
|
59
|
+
MERCURY_ENABLE_TELEGRAM=true
|
|
60
|
+
MERCURY_TELEGRAM_BOT_TOKEN=your-bot-token
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
Start:
|
|
62
64
|
|
|
63
65
|
```bash
|
|
64
66
|
mercury run
|
|
65
|
-
# or install as a background service:
|
|
67
|
+
# or install as a background service (macOS/Linux only):
|
|
66
68
|
mercury service install
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
> `mercury run` runs in the foreground (
|
|
71
|
+
> `mercury run` runs in the foreground — on Windows (WSL2) this is the only option. On macOS/Linux, prefer `mercury service install` for long-running use — it runs in the background and auto-restarts.
|
|
70
72
|
|
|
71
73
|
### Set up spaces and conversations
|
|
72
74
|
|
|
@@ -86,6 +88,26 @@ mercury link <id> main # Link a conversation to a space
|
|
|
86
88
|
|
|
87
89
|
Multiple conversations can point at the same space — they share memory, session, and vault.
|
|
88
90
|
|
|
91
|
+
### Updating Mercury
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 1. Stop Mercury
|
|
95
|
+
# macOS/Linux: mercury service uninstall
|
|
96
|
+
# Windows/WSL2: Ctrl+C in the terminal running mercury run
|
|
97
|
+
|
|
98
|
+
# 2. Update the package
|
|
99
|
+
npm update -g mercury-agent
|
|
100
|
+
|
|
101
|
+
# 3. Rebuild the agent container image
|
|
102
|
+
mercury build
|
|
103
|
+
|
|
104
|
+
# 4. Start again
|
|
105
|
+
# macOS/Linux: mercury service install
|
|
106
|
+
# Windows/WSL2: mercury run
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`mercury build` rebuilds the base agent image from the installed package source. The derived image (base + your extensions) rebuilds automatically on startup when the base changes.
|
|
110
|
+
|
|
89
111
|
---
|
|
90
112
|
|
|
91
113
|
## How It Works
|
|
@@ -445,6 +467,10 @@ This fork is maintained by [Avishai Tsabari](https://github.com/Avishai-Tsabari)
|
|
|
445
467
|
|
|
446
468
|
MIT — see [LICENSE](LICENSE)
|
|
447
469
|
|
|
470
|
+
### Third-party licenses
|
|
471
|
+
|
|
472
|
+
The WhatsApp adapter depends on [@whiskeysockets/baileys](https://github.com/WhiskeySockets/Baileys), which transitively includes [libsignal](https://github.com/nicktrav/libsignal) (GPL-3.0) for Signal Protocol encryption. This component runs inside an isolated Docker container and is loaded as a runtime dependency, not statically linked into Mercury's codebase. If your use case has strict copyleft concerns, consult legal counsel or omit the WhatsApp adapter.
|
|
473
|
+
|
|
448
474
|
---
|
|
449
475
|
|
|
450
476
|
<p align="center">
|
package/container/Dockerfile
CHANGED
|
@@ -60,7 +60,7 @@ RUN echo '{"args":["--no-sandbox"]}' > /home/mercury/.puppeteerrc.json
|
|
|
60
60
|
ENV CHROMIUM_FLAGS="--no-sandbox"
|
|
61
61
|
|
|
62
62
|
# Install CLIs
|
|
63
|
-
RUN bun add -g @
|
|
63
|
+
RUN bun add -g @earendil-works/pi-coding-agent@~0.79.6
|
|
64
64
|
|
|
65
65
|
WORKDIR /app
|
|
66
66
|
|
|
@@ -77,7 +77,7 @@ function patch(dir) {
|
|
|
77
77
|
const p = path.join(dir, f);
|
|
78
78
|
try {
|
|
79
79
|
if (fs.statSync(p).isDirectory()) patch(p);
|
|
80
|
-
else if (f === 'google.js' && p.includes('@
|
|
80
|
+
else if (f === 'google.js' && p.includes('@earendil-works/pi-ai')) {
|
|
81
81
|
let c = fs.readFileSync(p, 'utf8');
|
|
82
82
|
const noThinking = '&& !model.id.startsWith("gemma")';
|
|
83
83
|
const p1 = 'if (options.thinking?.enabled && model.reasoning) {';
|
|
@@ -97,6 +97,13 @@ patch('/home/mercury/.bun');
|
|
|
97
97
|
patch('/app/node_modules');
|
|
98
98
|
EOF
|
|
99
99
|
|
|
100
|
+
# Fix ownership of all mercury home dir artifacts before switching user.
|
|
101
|
+
# Placed here — after the last step that writes to /home/mercury (the pi patch),
|
|
102
|
+
# but before the volatile /app source COPYs below — so this expensive `chown -R`
|
|
103
|
+
# (it walks the whole Chromium + .bun tree) lands in a stable cached layer.
|
|
104
|
+
# Editing source files no longer invalidates it or forces the huge layer re-export.
|
|
105
|
+
RUN chown -R mercury:mercury /home/mercury
|
|
106
|
+
|
|
100
107
|
COPY src/agent/container-entry.ts /app/src/agent/container-entry.ts
|
|
101
108
|
COPY src/agent/model-capabilities-core.ts /app/src/agent/model-capabilities-core.ts
|
|
102
109
|
COPY src/agent/pi-failure-class.ts /app/src/agent/pi-failure-class.ts
|
|
@@ -119,9 +126,6 @@ RUN while IFS= read -r ext || [ -n "$ext" ]; do \
|
|
|
119
126
|
RUN echo '#!/bin/sh\nbun run /app/src/cli/mrctl.ts "$@"' > /usr/local/bin/mrctl && \
|
|
120
127
|
chmod +x /usr/local/bin/mrctl
|
|
121
128
|
|
|
122
|
-
# Fix ownership of all mercury home dir artifacts before switching user
|
|
123
|
-
RUN chown -R mercury:mercury /home/mercury
|
|
124
|
-
|
|
125
129
|
USER mercury
|
|
126
130
|
|
|
127
131
|
ENTRYPOINT ["bun", "run", "/app/src/agent/container-entry.ts"]
|
|
@@ -42,7 +42,7 @@ RUN echo '{"args":["--no-sandbox"]}' > /home/mercury/.puppeteerrc.json
|
|
|
42
42
|
ENV CHROMIUM_FLAGS="--no-sandbox"
|
|
43
43
|
|
|
44
44
|
# Install CLIs
|
|
45
|
-
RUN bun add -g @
|
|
45
|
+
RUN bun add -g @earendil-works/pi-coding-agent@~0.79.6
|
|
46
46
|
|
|
47
47
|
WORKDIR /app
|
|
48
48
|
|
|
@@ -59,7 +59,7 @@ function patch(dir) {
|
|
|
59
59
|
const p = path.join(dir, f);
|
|
60
60
|
try {
|
|
61
61
|
if (fs.statSync(p).isDirectory()) patch(p);
|
|
62
|
-
else if (f === 'google.js' && p.includes('@
|
|
62
|
+
else if (f === 'google.js' && p.includes('@earendil-works/pi-ai')) {
|
|
63
63
|
let c = fs.readFileSync(p, 'utf8');
|
|
64
64
|
const noThinking = '&& !model.id.startsWith("gemma")';
|
|
65
65
|
const p1 = 'if (options.thinking?.enabled && model.reasoning) {';
|
package/container/build.sh
CHANGED
|
@@ -9,46 +9,15 @@ cd "$PROJECT_ROOT"
|
|
|
9
9
|
IMAGE_NAME="mercury-agent"
|
|
10
10
|
|
|
11
11
|
# Parse arguments
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
BUILD_MINIMAL=false
|
|
15
|
-
|
|
16
|
-
if [ $# -eq 0 ]; then
|
|
17
|
-
BUILD_LATEST=true
|
|
18
|
-
elif [ "$1" = "all" ]; then
|
|
19
|
-
BUILD_ALL=true
|
|
20
|
-
elif [ "$1" = "latest" ]; then
|
|
21
|
-
BUILD_LATEST=true
|
|
22
|
-
elif [ "$1" = "minimal" ]; then
|
|
23
|
-
BUILD_MINIMAL=true
|
|
24
|
-
else
|
|
25
|
-
echo "Usage: $0 [all|latest|minimal]"
|
|
26
|
-
echo ""
|
|
27
|
-
echo "Presets:"
|
|
28
|
-
echo " latest Full devcontainer with Node, Python, Go, git (~2.8GB)"
|
|
29
|
-
echo " minimal Bun + pi + browser only (~1.9GB)"
|
|
30
|
-
echo " all Build both presets"
|
|
12
|
+
if [ $# -gt 0 ] && [ "$1" != "latest" ]; then
|
|
13
|
+
echo "Usage: $0 [latest]"
|
|
31
14
|
echo ""
|
|
32
|
-
echo "
|
|
15
|
+
echo "Builds ${IMAGE_NAME}:latest — full devcontainer with Node, Python, Go, git (~2.8GB)."
|
|
33
16
|
exit 1
|
|
34
17
|
fi
|
|
35
18
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
echo ""
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
if [ "$BUILD_ALL" = true ] || [ "$BUILD_MINIMAL" = true ]; then
|
|
44
|
-
echo "Building ${IMAGE_NAME}:minimal (bun-only)..."
|
|
45
|
-
docker build -f container/Dockerfile.minimal -t "${IMAGE_NAME}:minimal" .
|
|
46
|
-
echo "✓ Built ${IMAGE_NAME}:minimal"
|
|
47
|
-
echo ""
|
|
48
|
-
fi
|
|
49
|
-
|
|
19
|
+
echo "Building ${IMAGE_NAME}:latest (full devcontainer)..."
|
|
20
|
+
docker build -f container/Dockerfile -t "${IMAGE_NAME}:latest" .
|
|
21
|
+
echo "✓ Built ${IMAGE_NAME}:latest"
|
|
22
|
+
echo ""
|
|
50
23
|
echo "Build complete!"
|
|
51
|
-
if [ "$BUILD_ALL" = true ]; then
|
|
52
|
-
echo " ${IMAGE_NAME}:latest - Full devcontainer (~2.8GB)"
|
|
53
|
-
echo " ${IMAGE_NAME}:minimal - Bun + pi + browser (~1.9GB)"
|
|
54
|
-
fi
|
|
@@ -52,7 +52,7 @@ defaults:
|
|
|
52
52
|
trigger_patterns: always
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
Apply it with `mercury profile
|
|
55
|
+
Apply it with `mercury setup --profile <name|path>`. That validates
|
|
56
56
|
capabilities, copies extensions/AGENTS.md, and persists activation to
|
|
57
57
|
`.mercury/active-profile.json`, which Mercury loads at startup.
|
|
58
58
|
|
|
@@ -168,7 +168,7 @@ Depend on this repo (`mercury-agent`) for the types
|
|
|
168
168
|
|
|
169
169
|
## 5. Local test loop
|
|
170
170
|
|
|
171
|
-
1. `mercury profile
|
|
171
|
+
1. `mercury setup --profile ./barber`
|
|
172
172
|
2. `mercury service install` (builds the derived image with the extension CLI)
|
|
173
173
|
3. DM the bot as a non-admin number; confirm you can `book`/`cancel` only your
|
|
174
174
|
own appointments and cannot reach `gws`/Gmail.
|
|
@@ -175,9 +175,8 @@ Message received
|
|
|
175
175
|
# Set container timeout to 10 minutes
|
|
176
176
|
export MERCURY_CONTAINER_TIMEOUT_MS=600000
|
|
177
177
|
|
|
178
|
-
# Use
|
|
178
|
+
# Use the preset image from GitHub Container Registry
|
|
179
179
|
export MERCURY_AGENT_IMAGE=ghcr.io/avishai-tsabari/mercury-agent:latest # Full (default)
|
|
180
|
-
export MERCURY_AGENT_IMAGE=ghcr.io/avishai-tsabari/mercury-agent:minimal # Lightweight
|
|
181
180
|
```
|
|
182
181
|
|
|
183
182
|
## Sandboxing (Bubblewrap)
|
|
@@ -198,27 +197,24 @@ If you see `bwrap: Creating new namespace failed: Operation not permitted`, try
|
|
|
198
197
|
|
|
199
198
|
Custom images must install `bubblewrap` for sandboxing to work.
|
|
200
199
|
|
|
201
|
-
## Agent Image
|
|
200
|
+
## Agent Image Preset
|
|
202
201
|
|
|
203
|
-
Mercury publishes
|
|
202
|
+
Mercury publishes an image preset to GitHub Container Registry:
|
|
204
203
|
|
|
205
204
|
| Preset | Size | Contents |
|
|
206
205
|
|--------|------|----------|
|
|
207
206
|
| `ghcr.io/avishai-tsabari/mercury-agent:latest` | ~2.8GB | Full devcontainer: Bun, Node.js, Python, Go, git, build tools |
|
|
208
|
-
| `ghcr.io/avishai-tsabari/mercury-agent:minimal` | ~1.9GB | Lightweight runtime: Bun + pi + Chromium deps |
|
|
209
207
|
|
|
210
|
-
Images are published on each release. Version-specific tags are also available (e.g., `:0.2.0
|
|
208
|
+
Images are published on each release. Version-specific tags are also available (e.g., `:0.2.0`).
|
|
211
209
|
|
|
212
210
|
### Building Locally
|
|
213
211
|
|
|
214
|
-
To build
|
|
212
|
+
To build the image locally instead of pulling from the registry:
|
|
215
213
|
```bash
|
|
216
|
-
./container/build.sh
|
|
217
|
-
./container/build.sh latest # Full image only (default)
|
|
218
|
-
./container/build.sh minimal # Lightweight image only
|
|
214
|
+
./container/build.sh # Full image (default)
|
|
219
215
|
```
|
|
220
216
|
|
|
221
|
-
Then use `mercury-agent:latest`
|
|
217
|
+
Then use `mercury-agent:latest` (without the ghcr.io prefix).
|
|
222
218
|
|
|
223
219
|
## Custom Agent Images
|
|
224
220
|
|
package/docs/memory.md
CHANGED
|
@@ -107,12 +107,6 @@ _2026-02-25:_ Changed venue to [[Cafe Nimrod]].
|
|
|
107
107
|
- **Body** — Accumulated context (append semantics, timestamped)
|
|
108
108
|
- **Wikilinks** — Connections to other entities
|
|
109
109
|
|
|
110
|
-
## Conditional Context
|
|
111
|
-
|
|
112
|
-
Mercury can skip loading the full session for standalone prompts (e.g. "what's 2+2?"), reducing token usage. After the run, the prompt and reply are merged back into the session so history stays complete.
|
|
113
|
-
|
|
114
|
-
See [conditional-context.md](conditional-context.md) for details and configuration.
|
|
115
|
-
|
|
116
110
|
## Persistence
|
|
117
111
|
|
|
118
112
|
Memory persists because the agent writes to disk during conversation. When a session compacts or restarts, the vault files remain — the agent reads them fresh on next interaction.
|
package/docs/prd-config-load.md
CHANGED
|
@@ -71,7 +71,7 @@ For each mapped setting, `mergeRawMercuryConfig` applies:
|
|
|
71
71
|
| YAML parse, Zod file schema, flatten, merge | `src/config-file.ts` |
|
|
72
72
|
| Shared model-leg validation | `src/config-model-chain.ts` |
|
|
73
73
|
| `loadConfig`, Zod app schema | `src/config.ts` |
|
|
74
|
-
| Tests | `tests/config.test.ts` (+ guards in `router.test.ts
|
|
74
|
+
| Tests | `tests/config.test.ts` (+ guards in `router.test.ts`) |
|
|
75
75
|
| Template | `resources/templates/mercury.example.yaml` |
|
|
76
76
|
| Operator guide | `docs/configuration.md` |
|
|
77
77
|
|
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
unlinkSync,
|
|
9
9
|
writeFileSync,
|
|
10
10
|
} from "node:fs";
|
|
11
|
+
import { createRequire } from "node:module";
|
|
11
12
|
import { homedir, tmpdir } from "node:os";
|
|
12
|
-
import { delimiter, join } from "node:path";
|
|
13
|
+
import { delimiter, dirname, join } from "node:path";
|
|
13
14
|
|
|
14
15
|
const KNOWLEDGE_DIR = "knowledge";
|
|
15
16
|
const VAULT_DIRS = ["people", "projects", "references", "daily", "episodes", "weekly", "monthly", "templates"];
|
|
@@ -135,25 +136,31 @@ function exportMessages(
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
/**
|
|
138
|
-
* Build a child env whose PATH also contains the
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* `.bun/bin` but not the npm global bin). A bare `spawn("pi")` then fails with
|
|
144
|
-
* ENOENT before pi ever starts. Appending the well-known global-bin dirs makes
|
|
145
|
-
* resolution robust on local installs and is a no-op in the cloud.
|
|
139
|
+
* Build a child env whose PATH also contains directories where the `pi` CLI
|
|
140
|
+
* may live. A global npm install only links the top-level package's bins
|
|
141
|
+
* (mercury, mercury-ctl) — dependency bins like `pi` stay in node_modules/.bin.
|
|
142
|
+
* We resolve that .bin dir from the package graph first, then fall back to
|
|
143
|
+
* well-known global-bin dirs (~/.bun/bin, /usr/local/bin).
|
|
146
144
|
*/
|
|
147
145
|
function envWithPiOnPath(): NodeJS.ProcessEnv {
|
|
148
146
|
const isWindows = process.platform === "win32";
|
|
149
147
|
const base = process.env.PATH ?? process.env.Path ?? "";
|
|
150
148
|
const home = homedir();
|
|
149
|
+
|
|
150
|
+
let piNodeModulesBin: string | undefined;
|
|
151
|
+
try {
|
|
152
|
+
const req = createRequire(import.meta.url);
|
|
153
|
+
const pkgJson = req.resolve("@earendil-works/pi-coding-agent/package.json");
|
|
154
|
+
piNodeModulesBin = join(dirname(dirname(dirname(pkgJson))), ".bin");
|
|
155
|
+
} catch {}
|
|
156
|
+
|
|
151
157
|
const candidates = [
|
|
158
|
+
piNodeModulesBin,
|
|
152
159
|
join(home, ".bun", "bin"),
|
|
153
160
|
isWindows
|
|
154
161
|
? join(process.env.APPDATA ?? join(home, "AppData", "Roaming"), "npm")
|
|
155
162
|
: "/usr/local/bin",
|
|
156
|
-
];
|
|
163
|
+
].filter(Boolean) as string[];
|
|
157
164
|
// PATH entries are case-insensitive on Windows, case-sensitive on POSIX.
|
|
158
165
|
const normalize = (p: string) => (isWindows ? p.toLowerCase() : p);
|
|
159
166
|
const existing = new Set(base.split(delimiter).map(normalize).filter(Boolean));
|
|
@@ -45,20 +45,20 @@ Every entity file follows this shape. The **current** value of a fact lives in B
|
|
|
45
45
|
|
|
46
46
|
```markdown
|
|
47
47
|
---
|
|
48
|
-
name:
|
|
48
|
+
name: Alex Carter
|
|
49
49
|
type: person
|
|
50
50
|
updated: 2026-05-10
|
|
51
|
-
summary: Biotech investor in the
|
|
51
|
+
summary: Biotech investor in the research group; bullish on ACME.
|
|
52
52
|
# Structured "current value" fields for facts that are known to change:
|
|
53
|
-
|
|
53
|
+
acme_price_target: "$15"
|
|
54
54
|
---
|
|
55
55
|
|
|
56
|
-
#
|
|
56
|
+
# Alex Carter
|
|
57
57
|
|
|
58
|
-
Member of the [[
|
|
58
|
+
Member of the [[research-space]] investing group.
|
|
59
59
|
|
|
60
60
|
## Current View
|
|
61
|
-
-
|
|
61
|
+
- ACME price target: **$15** (as of 2026-05-10)
|
|
62
62
|
- Thesis: bullish, waiting for the Phase 3 readout
|
|
63
63
|
|
|
64
64
|
## History
|