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
@@ -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.
@@ -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`**.