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
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
# Authoring Applicative Profiles
|
|
2
|
-
|
|
3
|
-
The contract for building a **profile** — a package of deterministic business
|
|
4
|
-
logic that wraps raw capabilities (Calendar, email, …) and scopes what members
|
|
5
|
-
can do. Mercury (this repo) provides the schema, loader, permission scoping, and
|
|
6
|
-
the host-side capability broker; **profiles live in a separate repo** and depend
|
|
7
|
-
only on this contract.
|
|
8
|
-
|
|
9
|
-
> Layers: **Mercury** = platform (messaging, spaces, permissions, tokens,
|
|
10
|
-
> broker). **Capability extensions** = raw API access (e.g. `gws`), admin-only.
|
|
11
|
-
> **Profiles** = deterministic domain logic wrapping capabilities, member-facing.
|
|
12
|
-
|
|
13
|
-
## 1. Profile manifest — `mercury-profile.yaml`
|
|
14
|
-
|
|
15
|
-
```yaml
|
|
16
|
-
name: barber-appointments # lowercase-kebab; required
|
|
17
|
-
description: Appointment booking for a barber shop
|
|
18
|
-
version: 0.1.0
|
|
19
|
-
|
|
20
|
-
agents_md: ./AGENTS.md # optional; copied to the global AGENTS.md
|
|
21
|
-
|
|
22
|
-
# Raw capability extensions this profile requires to be installed. Validated at
|
|
23
|
-
# apply time — activation fails loudly if any are missing.
|
|
24
|
-
capabilities:
|
|
25
|
-
- gws
|
|
26
|
-
|
|
27
|
-
# Extensions this profile ships (copied into .mercury/extensions).
|
|
28
|
-
extensions:
|
|
29
|
-
- name: barber
|
|
30
|
-
source: ./extensions/barber
|
|
31
|
-
|
|
32
|
-
# EXHAUSTIVE member permission set while active. When present it REPLACES the
|
|
33
|
-
# default member permissions — nothing is merged in — so raw capabilities stay
|
|
34
|
-
# admin-only unless listed here. Omit entirely to keep built-in defaults.
|
|
35
|
-
member_permissions:
|
|
36
|
-
- prompt
|
|
37
|
-
- prefs.get
|
|
38
|
-
- barber # the profile's own capability, NOT gws
|
|
39
|
-
|
|
40
|
-
# Optional env the profile needs (host-side credentials for capabilities).
|
|
41
|
-
env:
|
|
42
|
-
- key: MERCURY_BARBER_BUSINESS_HOURS
|
|
43
|
-
description: "Business hours"
|
|
44
|
-
default: "09:00-18:00"
|
|
45
|
-
|
|
46
|
-
# Project-wide agent persona, injected into every container's system prompt.
|
|
47
|
-
system_prompt: |
|
|
48
|
-
You are a barber shop assistant. Help each customer book, view, and cancel
|
|
49
|
-
ONLY their own appointments. Never reveal other customers' schedules.
|
|
50
|
-
|
|
51
|
-
defaults:
|
|
52
|
-
trigger_patterns: always
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Apply it with `mercury profile apply <name|path|git-url>`. That validates
|
|
56
|
-
capabilities, copies extensions/AGENTS.md, and persists activation to
|
|
57
|
-
`.mercury/active-profile.json`, which Mercury loads at startup.
|
|
58
|
-
|
|
59
|
-
## 2. The capability broker (where the business logic runs)
|
|
60
|
-
|
|
61
|
-
A profile's privileged work runs **on the host**, so credentials never enter the
|
|
62
|
-
customer-controlled agent container. The profile's extension registers a
|
|
63
|
-
handler; the agent invokes it through a thin CLI.
|
|
64
|
-
|
|
65
|
-
### Register a handler (in the extension's `index.ts`)
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
import type { ExtensionSetupFn } from "mercury-agent"; // types from this repo
|
|
69
|
-
|
|
70
|
-
const setup: ExtensionSetupFn = (mercury) => {
|
|
71
|
-
// Make the capability gate-able. Members get it via member_permissions.
|
|
72
|
-
mercury.permission({ defaultRoles: [] });
|
|
73
|
-
|
|
74
|
-
// Host-side credentials for the wrapped capability (never sent to the container).
|
|
75
|
-
mercury.env({ from: "MERCURY_BARBER_GOOGLE_CREDENTIALS" });
|
|
76
|
-
|
|
77
|
-
mercury.capability("barber", async (req, ctx) => {
|
|
78
|
-
// req.callerId is the TOKEN-DERIVED, unspoofable caller — safe for ownership.
|
|
79
|
-
switch (req.action) {
|
|
80
|
-
case "availability":
|
|
81
|
-
return { data: await listOpenSlots(req.body, ctx) };
|
|
82
|
-
case "book":
|
|
83
|
-
return { data: await book(req.callerId, req.body, ctx) };
|
|
84
|
-
case "cancel":
|
|
85
|
-
return { data: await cancelOwn(req.callerId, req.body, ctx) };
|
|
86
|
-
case "my-appointments":
|
|
87
|
-
return { data: await listOwn(req.callerId, ctx) };
|
|
88
|
-
default:
|
|
89
|
-
return { status: 400, data: { error: `unknown action: ${req.action}` } };
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export default setup;
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### The contract types (exported from this repo)
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
type CapabilityHandler = (
|
|
101
|
-
req: CapabilityRequest,
|
|
102
|
-
ctx: MercuryExtensionContext,
|
|
103
|
-
) => Promise<CapabilityResult>;
|
|
104
|
-
|
|
105
|
-
interface CapabilityRequest {
|
|
106
|
-
name: string; // capability name
|
|
107
|
-
action: string; // sub-action (book, cancel, …)
|
|
108
|
-
callerId: string; // token-derived, TRUSTWORTHY — use for ownership checks
|
|
109
|
-
spaceId: string;
|
|
110
|
-
body: unknown; // parsed JSON request body (or null)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
interface CapabilityResult {
|
|
114
|
-
status?: number; // default 200
|
|
115
|
-
data: unknown; // JSON-serializable response
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
`ctx` (`MercuryExtensionContext`) gives you `db`, `config`, `log`, and
|
|
120
|
-
`hasCallerPermission(spaceId, callerId, permission)`. Store per-profile state via
|
|
121
|
-
`mercury.store` (namespaced KV) or your own tables through `ctx.db`.
|
|
122
|
-
|
|
123
|
-
### How the agent invokes it
|
|
124
|
-
|
|
125
|
-
Inside the container the agent runs:
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
mrctl capability barber book '{"slot":"2026-07-02T15:00"}'
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
→ `POST /api/capability/barber/book`. Document these commands for the agent in
|
|
132
|
-
the extension's `SKILL.md` or the profile's `AGENTS.md`.
|
|
133
|
-
|
|
134
|
-
## 3. Permission & security model (non-negotiable)
|
|
135
|
-
|
|
136
|
-
- **`member_permissions` is exhaustive.** List every permission a member may
|
|
137
|
-
hold, including the capability name (e.g. `barber`). Anything not listed —
|
|
138
|
-
including raw capabilities like `gws` — is unavailable to members.
|
|
139
|
-
- **Authorization = permission named after the capability.** The broker route
|
|
140
|
-
requires the caller to hold the `<name>` permission; the same grant gates both
|
|
141
|
-
the `mrctl capability <name> …` CLI and the route. Keep capability name ==
|
|
142
|
-
permission name == the entry in `member_permissions`.
|
|
143
|
-
- **Credentials stay on the host.** Put capability credentials in host env
|
|
144
|
-
(`mercury.env`) / `mercury.store` and use them only inside the handler. Never
|
|
145
|
-
expose a raw capability CLI (e.g. `gws`) to members — its secret would be
|
|
146
|
-
readable inside the container.
|
|
147
|
-
- **Enforce ownership with `req.callerId`.** It is token-derived and cannot be
|
|
148
|
-
spoofed by the container. Never trust an id passed in `req.body`.
|
|
149
|
-
- **`callerId === "system"`** for scheduled/system runs — handle it explicitly
|
|
150
|
-
(e.g. skip per-customer ownership).
|
|
151
|
-
|
|
152
|
-
## 4. Suggested profile repo layout
|
|
153
|
-
|
|
154
|
-
```
|
|
155
|
-
<profile-repo>/
|
|
156
|
-
barber/ # one folder per profile
|
|
157
|
-
mercury-profile.yaml
|
|
158
|
-
AGENTS.md
|
|
159
|
-
extensions/
|
|
160
|
-
barber/
|
|
161
|
-
index.ts # registers permission + capability
|
|
162
|
-
package.json
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
Depend on this repo (`mercury-agent`) for the types
|
|
166
|
-
(`ExtensionSetupFn`, `CapabilityRequest`, `CapabilityResult`,
|
|
167
|
-
`MercuryExtensionContext`). See `src/extensions/types.ts` for the full surface.
|
|
168
|
-
|
|
169
|
-
## 5. Local test loop
|
|
170
|
-
|
|
171
|
-
1. `mercury profile apply ./barber`
|
|
172
|
-
2. `mercury service install` (builds the derived image with the extension CLI)
|
|
173
|
-
3. DM the bot as a non-admin number; confirm you can `book`/`cancel` only your
|
|
174
|
-
own appointments and cannot reach `gws`/Gmail.
|
|
1
|
+
# Authoring Applicative Profiles
|
|
2
|
+
|
|
3
|
+
The contract for building a **profile** — a package of deterministic business
|
|
4
|
+
logic that wraps raw capabilities (Calendar, email, …) and scopes what members
|
|
5
|
+
can do. Mercury (this repo) provides the schema, loader, permission scoping, and
|
|
6
|
+
the host-side capability broker; **profiles live in a separate repo** and depend
|
|
7
|
+
only on this contract.
|
|
8
|
+
|
|
9
|
+
> Layers: **Mercury** = platform (messaging, spaces, permissions, tokens,
|
|
10
|
+
> broker). **Capability extensions** = raw API access (e.g. `gws`), admin-only.
|
|
11
|
+
> **Profiles** = deterministic domain logic wrapping capabilities, member-facing.
|
|
12
|
+
|
|
13
|
+
## 1. Profile manifest — `mercury-profile.yaml`
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
name: barber-appointments # lowercase-kebab; required
|
|
17
|
+
description: Appointment booking for a barber shop
|
|
18
|
+
version: 0.1.0
|
|
19
|
+
|
|
20
|
+
agents_md: ./AGENTS.md # optional; copied to the global AGENTS.md
|
|
21
|
+
|
|
22
|
+
# Raw capability extensions this profile requires to be installed. Validated at
|
|
23
|
+
# apply time — activation fails loudly if any are missing.
|
|
24
|
+
capabilities:
|
|
25
|
+
- gws
|
|
26
|
+
|
|
27
|
+
# Extensions this profile ships (copied into .mercury/extensions).
|
|
28
|
+
extensions:
|
|
29
|
+
- name: barber
|
|
30
|
+
source: ./extensions/barber
|
|
31
|
+
|
|
32
|
+
# EXHAUSTIVE member permission set while active. When present it REPLACES the
|
|
33
|
+
# default member permissions — nothing is merged in — so raw capabilities stay
|
|
34
|
+
# admin-only unless listed here. Omit entirely to keep built-in defaults.
|
|
35
|
+
member_permissions:
|
|
36
|
+
- prompt
|
|
37
|
+
- prefs.get
|
|
38
|
+
- barber # the profile's own capability, NOT gws
|
|
39
|
+
|
|
40
|
+
# Optional env the profile needs (host-side credentials for capabilities).
|
|
41
|
+
env:
|
|
42
|
+
- key: MERCURY_BARBER_BUSINESS_HOURS
|
|
43
|
+
description: "Business hours"
|
|
44
|
+
default: "09:00-18:00"
|
|
45
|
+
|
|
46
|
+
# Project-wide agent persona, injected into every container's system prompt.
|
|
47
|
+
system_prompt: |
|
|
48
|
+
You are a barber shop assistant. Help each customer book, view, and cancel
|
|
49
|
+
ONLY their own appointments. Never reveal other customers' schedules.
|
|
50
|
+
|
|
51
|
+
defaults:
|
|
52
|
+
trigger_patterns: always
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Apply it with `mercury profile apply <name|path|git-url>`. That validates
|
|
56
|
+
capabilities, copies extensions/AGENTS.md, and persists activation to
|
|
57
|
+
`.mercury/active-profile.json`, which Mercury loads at startup.
|
|
58
|
+
|
|
59
|
+
## 2. The capability broker (where the business logic runs)
|
|
60
|
+
|
|
61
|
+
A profile's privileged work runs **on the host**, so credentials never enter the
|
|
62
|
+
customer-controlled agent container. The profile's extension registers a
|
|
63
|
+
handler; the agent invokes it through a thin CLI.
|
|
64
|
+
|
|
65
|
+
### Register a handler (in the extension's `index.ts`)
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import type { ExtensionSetupFn } from "mercury-agent"; // types from this repo
|
|
69
|
+
|
|
70
|
+
const setup: ExtensionSetupFn = (mercury) => {
|
|
71
|
+
// Make the capability gate-able. Members get it via member_permissions.
|
|
72
|
+
mercury.permission({ defaultRoles: [] });
|
|
73
|
+
|
|
74
|
+
// Host-side credentials for the wrapped capability (never sent to the container).
|
|
75
|
+
mercury.env({ from: "MERCURY_BARBER_GOOGLE_CREDENTIALS" });
|
|
76
|
+
|
|
77
|
+
mercury.capability("barber", async (req, ctx) => {
|
|
78
|
+
// req.callerId is the TOKEN-DERIVED, unspoofable caller — safe for ownership.
|
|
79
|
+
switch (req.action) {
|
|
80
|
+
case "availability":
|
|
81
|
+
return { data: await listOpenSlots(req.body, ctx) };
|
|
82
|
+
case "book":
|
|
83
|
+
return { data: await book(req.callerId, req.body, ctx) };
|
|
84
|
+
case "cancel":
|
|
85
|
+
return { data: await cancelOwn(req.callerId, req.body, ctx) };
|
|
86
|
+
case "my-appointments":
|
|
87
|
+
return { data: await listOwn(req.callerId, ctx) };
|
|
88
|
+
default:
|
|
89
|
+
return { status: 400, data: { error: `unknown action: ${req.action}` } };
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default setup;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### The contract types (exported from this repo)
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
type CapabilityHandler = (
|
|
101
|
+
req: CapabilityRequest,
|
|
102
|
+
ctx: MercuryExtensionContext,
|
|
103
|
+
) => Promise<CapabilityResult>;
|
|
104
|
+
|
|
105
|
+
interface CapabilityRequest {
|
|
106
|
+
name: string; // capability name
|
|
107
|
+
action: string; // sub-action (book, cancel, …)
|
|
108
|
+
callerId: string; // token-derived, TRUSTWORTHY — use for ownership checks
|
|
109
|
+
spaceId: string;
|
|
110
|
+
body: unknown; // parsed JSON request body (or null)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface CapabilityResult {
|
|
114
|
+
status?: number; // default 200
|
|
115
|
+
data: unknown; // JSON-serializable response
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`ctx` (`MercuryExtensionContext`) gives you `db`, `config`, `log`, and
|
|
120
|
+
`hasCallerPermission(spaceId, callerId, permission)`. Store per-profile state via
|
|
121
|
+
`mercury.store` (namespaced KV) or your own tables through `ctx.db`.
|
|
122
|
+
|
|
123
|
+
### How the agent invokes it
|
|
124
|
+
|
|
125
|
+
Inside the container the agent runs:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
mrctl capability barber book '{"slot":"2026-07-02T15:00"}'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
→ `POST /api/capability/barber/book`. Document these commands for the agent in
|
|
132
|
+
the extension's `SKILL.md` or the profile's `AGENTS.md`.
|
|
133
|
+
|
|
134
|
+
## 3. Permission & security model (non-negotiable)
|
|
135
|
+
|
|
136
|
+
- **`member_permissions` is exhaustive.** List every permission a member may
|
|
137
|
+
hold, including the capability name (e.g. `barber`). Anything not listed —
|
|
138
|
+
including raw capabilities like `gws` — is unavailable to members.
|
|
139
|
+
- **Authorization = permission named after the capability.** The broker route
|
|
140
|
+
requires the caller to hold the `<name>` permission; the same grant gates both
|
|
141
|
+
the `mrctl capability <name> …` CLI and the route. Keep capability name ==
|
|
142
|
+
permission name == the entry in `member_permissions`.
|
|
143
|
+
- **Credentials stay on the host.** Put capability credentials in host env
|
|
144
|
+
(`mercury.env`) / `mercury.store` and use them only inside the handler. Never
|
|
145
|
+
expose a raw capability CLI (e.g. `gws`) to members — its secret would be
|
|
146
|
+
readable inside the container.
|
|
147
|
+
- **Enforce ownership with `req.callerId`.** It is token-derived and cannot be
|
|
148
|
+
spoofed by the container. Never trust an id passed in `req.body`.
|
|
149
|
+
- **`callerId === "system"`** for scheduled/system runs — handle it explicitly
|
|
150
|
+
(e.g. skip per-customer ownership).
|
|
151
|
+
|
|
152
|
+
## 4. Suggested profile repo layout
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
<profile-repo>/
|
|
156
|
+
barber/ # one folder per profile
|
|
157
|
+
mercury-profile.yaml
|
|
158
|
+
AGENTS.md
|
|
159
|
+
extensions/
|
|
160
|
+
barber/
|
|
161
|
+
index.ts # registers permission + capability
|
|
162
|
+
package.json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Depend on this repo (`mercury-agent`) for the types
|
|
166
|
+
(`ExtensionSetupFn`, `CapabilityRequest`, `CapabilityResult`,
|
|
167
|
+
`MercuryExtensionContext`). See `src/extensions/types.ts` for the full surface.
|
|
168
|
+
|
|
169
|
+
## 5. Local test loop
|
|
170
|
+
|
|
171
|
+
1. `mercury profile apply ./barber`
|
|
172
|
+
2. `mercury service install` (builds the derived image with the extension CLI)
|
|
173
|
+
3. DM the bot as a non-admin number; confirm you can `book`/`cancel` only your
|
|
174
|
+
own appointments and cannot reach `gws`/Gmail.
|
package/docs/configuration.md
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
# Configuration
|
|
2
|
-
|
|
3
|
-
Mercury reads settings from **environment variables** (`MERCURY_*`) and, optionally, a project **`mercury.yaml`** file in the current working directory.
|
|
4
|
-
|
|
5
|
-
**Product / design spec:** [prd-config-load.md](prd-config-load.md) (config load: YAML + env precedence, security, non-goals).
|
|
6
|
-
|
|
7
|
-
## Precedence
|
|
8
|
-
|
|
9
|
-
1. If a `MERCURY_*` variable is **set** in the environment (the key exists, including empty string), its value wins.
|
|
10
|
-
2. Otherwise, if **`mercury.yaml`** (or **`mercury.yml`**) exists in `cwd`, values from that file apply.
|
|
11
|
-
3. Otherwise, built-in defaults from `config.ts` apply.
|
|
12
|
-
|
|
13
|
-
Set **`MERCURY_CONFIG_FILE`** to an explicit path to load a different file. Set it to **`""`** (empty) or **`none`** to **disable** loading any file (useful for tests or when you want env/defaults only).
|
|
14
|
-
|
|
15
|
-
Relative paths in `MERCURY_CONFIG_FILE` are resolved against `cwd`.
|
|
16
|
-
|
|
17
|
-
## Secrets (never use `mercury.yaml` for these)
|
|
18
|
-
|
|
19
|
-
These must be supplied via environment variables only; they are **not** read from YAML:
|
|
20
|
-
|
|
21
|
-
- `MERCURY_API_SECRET`
|
|
22
|
-
- `MERCURY_CHAT_API_KEY`
|
|
23
|
-
- `MERCURY_DISCORD_GATEWAY_SECRET`
|
|
24
|
-
|
|
25
|
-
Platform tokens, provider API keys, and extension keys (e.g. `MERCURY_TELEGRAM_BOT_TOKEN`, `MERCURY_BRAVE_API_KEY`) are also **env-only** today—they are not part of the YAML schema.
|
|
26
|
-
|
|
27
|
-
## YAML layout
|
|
28
|
-
|
|
29
|
-
See [`resources/templates/mercury.example.yaml`](../resources/templates/mercury.example.yaml) for a commented template. Supported sections include `server`, `model`, `ingress`, `runtime`, `trigger`, `context`, `conditional_context`, `compaction`, `agent`, `discord`, `telegram`, `media`, and `permissions`.
|
|
30
|
-
|
|
31
|
-
The `context:` block seeds default conversation-context behavior into the `main` space on first boot:
|
|
32
|
-
|
|
33
|
-
```yaml
|
|
34
|
-
context:
|
|
35
|
-
mode: context # clear | context (default: context)
|
|
36
|
-
window_size: 10 # 1-50 (default: 10). Sliding-window turns when mode=context.
|
|
37
|
-
reply_chain_depth: 10 # 1-50 (default: 10). Reply chain depth when mode=clear.
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Per-space overrides via `mrctl config set context.<key> <value>` always win over YAML defaults; YAML re-reads on restart do not overwrite an existing space row.
|
|
41
|
-
|
|
42
|
-
You may also set a top-level **`model_chain`** array as an alias for `model.chain`.
|
|
43
|
-
|
|
44
|
-
## Model chain
|
|
45
|
-
|
|
46
|
-
In YAML, use a list of `{ provider, model }` objects under `model.chain` (max 4 legs). The same rules apply as for `MERCURY_MODEL_CHAIN` JSON.
|
|
47
|
-
|
|
48
|
-
Optional **`model.capabilities`** may be a mapping; it is applied like `MERCURY_MODEL_CAPABILITIES` JSON.
|
|
49
|
-
|
|
50
|
-
### Removed: `provider: cursor`
|
|
51
|
-
|
|
52
|
-
The **Cursor Agent CLI** integration has been removed. All model legs use **pi** with standard providers (`anthropic`, `openai`, `google`, `mistral`, `groq`, `openrouter`, etc.).
|
|
53
|
-
|
|
54
|
-
If your chain still has `provider: cursor`, the agent run **fails fast** with an error that points here. Switch to the **native provider** for the model you want (for example `anthropic` for Claude, `openai` for GPT) and set the matching **`MERCURY_*_API_KEY`**.
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Mercury reads settings from **environment variables** (`MERCURY_*`) and, optionally, a project **`mercury.yaml`** file in the current working directory.
|
|
4
|
+
|
|
5
|
+
**Product / design spec:** [prd-config-load.md](prd-config-load.md) (config load: YAML + env precedence, security, non-goals).
|
|
6
|
+
|
|
7
|
+
## Precedence
|
|
8
|
+
|
|
9
|
+
1. If a `MERCURY_*` variable is **set** in the environment (the key exists, including empty string), its value wins.
|
|
10
|
+
2. Otherwise, if **`mercury.yaml`** (or **`mercury.yml`**) exists in `cwd`, values from that file apply.
|
|
11
|
+
3. Otherwise, built-in defaults from `config.ts` apply.
|
|
12
|
+
|
|
13
|
+
Set **`MERCURY_CONFIG_FILE`** to an explicit path to load a different file. Set it to **`""`** (empty) or **`none`** to **disable** loading any file (useful for tests or when you want env/defaults only).
|
|
14
|
+
|
|
15
|
+
Relative paths in `MERCURY_CONFIG_FILE` are resolved against `cwd`.
|
|
16
|
+
|
|
17
|
+
## Secrets (never use `mercury.yaml` for these)
|
|
18
|
+
|
|
19
|
+
These must be supplied via environment variables only; they are **not** read from YAML:
|
|
20
|
+
|
|
21
|
+
- `MERCURY_API_SECRET`
|
|
22
|
+
- `MERCURY_CHAT_API_KEY`
|
|
23
|
+
- `MERCURY_DISCORD_GATEWAY_SECRET`
|
|
24
|
+
|
|
25
|
+
Platform tokens, provider API keys, and extension keys (e.g. `MERCURY_TELEGRAM_BOT_TOKEN`, `MERCURY_BRAVE_API_KEY`) are also **env-only** today—they are not part of the YAML schema.
|
|
26
|
+
|
|
27
|
+
## YAML layout
|
|
28
|
+
|
|
29
|
+
See [`resources/templates/mercury.example.yaml`](../resources/templates/mercury.example.yaml) for a commented template. Supported sections include `server`, `model`, `ingress`, `runtime`, `trigger`, `context`, `conditional_context`, `compaction`, `agent`, `discord`, `telegram`, `media`, and `permissions`.
|
|
30
|
+
|
|
31
|
+
The `context:` block seeds default conversation-context behavior into the `main` space on first boot:
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
context:
|
|
35
|
+
mode: context # clear | context (default: context)
|
|
36
|
+
window_size: 10 # 1-50 (default: 10). Sliding-window turns when mode=context.
|
|
37
|
+
reply_chain_depth: 10 # 1-50 (default: 10). Reply chain depth when mode=clear.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Per-space overrides via `mrctl config set context.<key> <value>` always win over YAML defaults; YAML re-reads on restart do not overwrite an existing space row.
|
|
41
|
+
|
|
42
|
+
You may also set a top-level **`model_chain`** array as an alias for `model.chain`.
|
|
43
|
+
|
|
44
|
+
## Model chain
|
|
45
|
+
|
|
46
|
+
In YAML, use a list of `{ provider, model }` objects under `model.chain` (max 4 legs). The same rules apply as for `MERCURY_MODEL_CHAIN` JSON.
|
|
47
|
+
|
|
48
|
+
Optional **`model.capabilities`** may be a mapping; it is applied like `MERCURY_MODEL_CAPABILITIES` JSON.
|
|
49
|
+
|
|
50
|
+
### Removed: `provider: cursor`
|
|
51
|
+
|
|
52
|
+
The **Cursor Agent CLI** integration has been removed. All model legs use **pi** with standard providers (`anthropic`, `openai`, `google`, `mistral`, `groq`, `openrouter`, etc.).
|
|
53
|
+
|
|
54
|
+
If your chain still has `provider: cursor`, the agent run **fails fast** with an error that points here. Switch to the **native provider** for the model you want (for example `anthropic` for Claude, `openai` for GPT) and set the matching **`MERCURY_*_API_KEY`**.
|