mercury-agent 0.16.0 → 0.16.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 (80) hide show
  1. package/README.md +6 -0
  2. package/container/Dockerfile +6 -1
  3. package/container/Dockerfile.base +6 -1
  4. package/docs/README.md +50 -0
  5. package/docs/authoring-profiles.md +8 -0
  6. package/docs/behavior-layers.md +193 -0
  7. package/docs/extensions.md +2 -0
  8. package/docs/goals/football-reporter-profile/decisions.md +479 -0
  9. package/docs/goals/football-reporter-profile/goal.md +91 -0
  10. package/docs/goals/football-reporter-profile/roadmap.md +196 -0
  11. package/docs/goals/whatsapp-bot-hardening/README.md +43 -0
  12. package/docs/goals/whatsapp-bot-hardening/archive/ambient-group-context.md +211 -0
  13. package/docs/goals/whatsapp-bot-hardening/archive/command-routing-consistency.md +210 -0
  14. package/docs/goals/whatsapp-bot-hardening/archive/destructive-command-guards.md +218 -0
  15. package/docs/goals/whatsapp-bot-hardening/archive/docs-extension.md +182 -0
  16. package/docs/goals/whatsapp-bot-hardening/archive/handoff-2026-08-09.md +588 -0
  17. package/docs/goals/whatsapp-bot-hardening/archive/media-size-and-silent-drop.md +178 -0
  18. package/docs/goals/whatsapp-bot-hardening/archive/member-memory.md +199 -0
  19. package/docs/goals/whatsapp-bot-hardening/archive/message-author-attribution.md +211 -0
  20. package/docs/goals/whatsapp-bot-hardening/archive/run-from-source-switchover.md +212 -0
  21. package/docs/goals/whatsapp-bot-hardening/archive/setup-plan-2026-08-06.md +1117 -0
  22. package/docs/goals/whatsapp-bot-hardening/decisions.md +200 -0
  23. package/docs/goals/whatsapp-bot-hardening/goal.md +75 -0
  24. package/docs/goals/whatsapp-bot-hardening/open-threads.md +396 -0
  25. package/docs/goals/whatsapp-bot-hardening/roadmap.md +245 -0
  26. package/docs/memory.md +30 -0
  27. package/docs/profile-guide.md +483 -0
  28. package/docs/refactor/.gitkeep +0 -0
  29. package/docs/refactor/archive/.gitkeep +0 -0
  30. package/docs/refactor/audits/.gitkeep +0 -0
  31. package/docs/refactor/backlog/.gitkeep +0 -0
  32. package/docs/skills-guide.md +154 -0
  33. package/examples/extensions/README.md +1 -0
  34. package/examples/extensions/feed-watch/config.ts +354 -0
  35. package/examples/extensions/feed-watch/digest.ts +344 -0
  36. package/examples/extensions/feed-watch/feeds.ts +341 -0
  37. package/examples/extensions/feed-watch/index.ts +302 -0
  38. package/examples/extensions/feed-watch/items.ts +171 -0
  39. package/examples/extensions/feed-watch/match.ts +107 -0
  40. package/examples/extensions/feed-watch/prompts/verify.md +35 -0
  41. package/examples/extensions/feed-watch/skill/SKILL.md +128 -0
  42. package/examples/extensions/feed-watch/watch.ts +548 -0
  43. package/examples/extensions/longview/hook.ts +179 -16
  44. package/examples/extensions/longview/index.ts +13 -2
  45. package/examples/extensions/longview/prompts/summarize.md +3 -2
  46. package/examples/extensions/longview/render/telegraph-nodes.ts +21 -1
  47. package/examples/extensions/longview/summarize.ts +57 -14
  48. package/examples/extensions/napkin/index.ts +334 -86
  49. package/examples/extensions/napkin/pi-spawn.ts +196 -0
  50. package/examples/extensions/pinchtab/index.ts +37 -4
  51. package/examples/extensions/pinchtab/lib/session-injector.ts +31 -9
  52. package/examples/extensions/pinchtab/skill/SKILL.md +29 -0
  53. package/examples/profiles/_template/AGENTS.md +138 -0
  54. package/examples/profiles/_template/README.md +65 -0
  55. package/examples/profiles/_template/config.yaml +57 -0
  56. package/examples/profiles/_template/tasks/daily.md +32 -0
  57. package/examples/profiles/football-reporter/AGENTS.md +219 -0
  58. package/examples/profiles/football-reporter/README.md +138 -0
  59. package/examples/profiles/football-reporter/config.yaml +174 -0
  60. package/examples/profiles/football-reporter/seed/MEMORY.md +42 -0
  61. package/examples/profiles/football-reporter/seed/episodes/barcelona-2026-27.md +25 -0
  62. package/examples/profiles/football-reporter/seed/episodes/beitar-jerusalem-2026-27.md +25 -0
  63. package/examples/profiles/football-reporter/seed/episodes/maccabi-haifa-2026-27.md +24 -0
  64. package/examples/profiles/football-reporter/seed/episodes/man-united-2026-27.md +26 -0
  65. package/examples/profiles/football-reporter/seed/episodes/real-madrid-2026-27.md +25 -0
  66. package/examples/profiles/football-reporter/seed/napkin-distill.md +55 -0
  67. package/examples/profiles/football-reporter/tasks/daily-article.md +142 -0
  68. package/package.json +8 -5
  69. package/src/adapters/whatsapp.ts +75 -13
  70. package/src/bridges/whatsapp.ts +6 -4
  71. package/src/cli/mrctl.ts +9 -1
  72. package/src/core/handler.ts +51 -6
  73. package/src/core/routes/dashboard.ts +106 -5
  74. package/src/core/routes/tasks.ts +28 -0
  75. package/src/core/runtime.ts +63 -2
  76. package/src/core/task-scheduler.ts +103 -12
  77. package/src/extensions/catalog.ts +9 -0
  78. package/src/profile/space-profile.ts +780 -0
  79. package/src/storage/db.ts +215 -0
  80. package/src/types.ts +56 -0
package/README.md CHANGED
@@ -217,6 +217,9 @@ mercury setup --profile https://github.com/user/mercury-stock-agent
217
217
  # Create from local directory
218
218
  mercury setup --profile ./my-profiles/ml-assistant
219
219
 
220
+ # Apply a profile non-interactively (deploy scripts, existing projects)
221
+ mercury profiles apply ./my-profiles/ml-assistant
222
+
220
223
  # List available profiles
221
224
  mercury profiles list
222
225
 
@@ -224,6 +227,8 @@ mercury profiles list
224
227
  mercury profiles export ./my-profile
225
228
  ```
226
229
 
230
+ Building one for a specific use-case — what goes in `AGENTS.md`, how to keep it accurate, memory, skills, tasks, permissions, testing — is covered in [docs/profile-guide.md](docs/profile-guide.md). A single space inside a general bot gets a **space profile** instead (`scripts/space-profile.ts`, scaffold in [`examples/profiles/_template/`](examples/profiles/_template/)).
231
+
227
232
  ---
228
233
 
229
234
  ## CLI
@@ -513,6 +518,7 @@ mrctl config set trigger_patterns "@Bot,Bot"
513
518
  - [Authentication](docs/auth/overview.md)
514
519
  - [Message pipeline](docs/pipeline.md)
515
520
  - [Memory system](docs/memory.md)
521
+ - **Building a bot for a use-case:** [Profile guide](docs/profile-guide.md) · [Behaviour layers](docs/behavior-layers.md) · [Skills guide](docs/skills-guide.md) · [Applicative profile contract](docs/authoring-profiles.md)
516
522
  - [Scheduled tasks](docs/scheduler.md)
517
523
  - [Permissions](docs/permissions.md)
518
524
  - [Media handling](docs/media/overview.md)
@@ -95,7 +95,12 @@ RUN bun install --production
95
95
  # but before the volatile /app source COPYs below — so this expensive `chown -R`
96
96
  # (it walks the whole Chromium + .bun tree) lands in a stable cached layer.
97
97
  # Editing source files no longer invalidates it or forces the huge layer re-export.
98
- RUN chown -R mercury:mercury /home/mercury
98
+ # `mkdir` here rather than leaving it to Docker: the host mounts the global dir
99
+ # into PI_CODING_AGENT_DIR entry by entry, so Docker would create this dir as
100
+ # the mount parent and own it as root — and pi 0.84's credential store writes
101
+ # an empty auth.json on its first *read*, which then fails EACCES and takes
102
+ # every model leg down with it. Folded into the chown so it shares the layer.
103
+ RUN mkdir -p /home/mercury/.pi/agent && chown -R mercury:mercury /home/mercury
99
104
 
100
105
  COPY src/agent/container-entry.ts /app/src/agent/container-entry.ts
101
106
  COPY src/agent/model-capabilities-core.ts /app/src/agent/model-capabilities-core.ts
@@ -94,7 +94,12 @@ RUN echo '#!/bin/sh\nbun run /app/src/cli/mrctl.ts "$@"' > /usr/local/bin/mrctl
94
94
  chmod +x /usr/local/bin/mrctl
95
95
 
96
96
  # Fix ownership of all mercury home dir artifacts before switching user
97
- RUN chown -R mercury:mercury /home/mercury
97
+ # `mkdir` here rather than leaving it to Docker: the host mounts the global dir
98
+ # into PI_CODING_AGENT_DIR entry by entry, so Docker would create this dir as
99
+ # the mount parent and own it as root — and pi 0.84's credential store writes
100
+ # an empty auth.json on its first *read*, which then fails EACCES and takes
101
+ # every model leg down with it. Folded into the chown so it shares the layer.
102
+ RUN mkdir -p /home/mercury/.pi/agent && chown -R mercury:mercury /home/mercury
98
103
 
99
104
  USER mercury
100
105
 
package/docs/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Mercury — docs
2
+
3
+ Product docs, planning docs, ops runbooks and workflow scaffolding for
4
+ [mercury](https://github.com/Avishai-Tsabari/mercury). All of it lives in this
5
+ repository, tracked normally.
6
+
7
+ ## Layout
8
+
9
+ ```
10
+ docs/
11
+ configuration.md, pipeline.md, auth/, security/, ... product & reference docs
12
+ runbooks/ operational runbooks (release steps, OAuth setup)
13
+ goals/{slug}/ multi-milestone goals (goal, roadmap, decisions)
14
+ ideas/{slug}.md raw ideas, not yet planned
15
+ backlog/{slug}.md goal + architecture approved, not started
16
+ in-progress/{slug}.md active implementation
17
+ archive/{slug}.md shipped or abandoned
18
+ bugs/{slug}.md open bug reports
19
+ debug/{severity}/ bug post-mortems
20
+ refactor/ audits, active refactor docs, archive
21
+ notes/, templates/, html-slides/, pending-updates/
22
+ ROADMAP.md single source of truth for priority
23
+ ```
24
+
25
+ ## Content rule
26
+
27
+ The repo is private, but "private" is not "unreviewed". For everything under
28
+ `docs/`:
29
+
30
+ - **Name secrets, never paste values.** `MERCURY_ANTHROPIC_API_KEY`, not the key.
31
+ - **Keep third-party identifiers out unless they carry the finding.** A phone
32
+ number, WhatsApp JID or LID belongs in a doc only when the identifier *is* the
33
+ evidence — a post-mortem about LID resolution may quote one; a setup guide may
34
+ not.
35
+
36
+ ## History
37
+
38
+ Until 2026-08-19 the scaffolding lived in a second private repository,
39
+ `saharw01/mercury-planning`, whose working tree *was* this directory — a
40
+ workaround from when mercury was public and planning docs were kept out of it by
41
+ policy (PR #22, 2026-08-10).
42
+
43
+ That is over. The repo is private, PRs #46/#47 brought `docs/debug/` and
44
+ `docs/runbooks/` back under version control, and the rest of the scaffolding
45
+ followed. The planning repo's 44 commits were merged in with `-X subtree=docs`,
46
+ so authorship and per-file history survive; `git log --follow` traces through
47
+ both the original subtree split (mercury `d5d2aa6`) and the re-merge.
48
+
49
+ There is one repo and one `git status` again. Nothing here needs a second remote,
50
+ a nested `.git`, or an allowlist in `.git/info/exclude`.
@@ -1,5 +1,13 @@
1
1
  # Authoring Applicative Profiles
2
2
 
3
+ > This page is the **contract** — manifest, broker, permissions. For *how to
4
+ > design* a profile that is accurate and stays in character (the layer map,
5
+ > facts inventory, `AGENTS.md` shape, memory, skills, tasks, testing) read
6
+ > [`profile-guide.md`](profile-guide.md) first; for what the model actually
7
+ > receives, [`behavior-layers.md`](behavior-layers.md). Per-space profiles
8
+ > (one space inside a general bot) use `scripts/space-profile.ts` instead of
9
+ > this manifest — see `examples/profiles/_template/`.
10
+
3
11
  The contract for building a **profile** — a package of deterministic business
4
12
  logic that wraps raw capabilities (Calendar, email, …) and scopes what members
5
13
  can do. Mercury (this repo) provides the schema, loader, permission scoping, and
@@ -0,0 +1,193 @@
1
+ # Behaviour layers — where a rule lives, and what the model actually receives
2
+
3
+ Every agent run is assembled from several layers written by different people
4
+ at different times: the platform prompt baked into the image, an optional
5
+ applicative profile, the owner's global character, the global `AGENTS.md`, the
6
+ space `AGENTS.md`, space preferences, space config, task prompts, and the
7
+ memory files in the workspace. Most "the bot ignores my instruction" reports
8
+ are two of these layers disagreeing. This page is the verified map — what is
9
+ sent, in what order, who owns it, and when a change takes effect.
10
+
11
+ Verified against `src/agent/container-entry.ts`, `src/core/runtime.ts`,
12
+ `src/storage/db.ts` and pi 0.84.2's `resource-loader.js` / `system-prompt.js`
13
+ (inside the agent image) on 2026-08-21. Re-verify by grepping the image, not
14
+ the repo, after a pi upgrade.
15
+
16
+ ## 1. The system prompt, top to bottom
17
+
18
+ ```
19
+ 1. pi preamble ............................ image container-entry.ts buildSystemPrompt
20
+ "You are an expert AI assistant…", tool list, "Be concise"
21
+ (override mode; OVERRIDE_PI_SYSTEM_PROMPT)
22
+ 2. Mercury platform block .................. image container-entry.ts buildMercuryAdditions
23
+ inbox/outbox · "You are <bot_username>; read /docs/mercury"
24
+ ## Permissions & Security ("Permission denied → simply inform the user")
25
+ ## Moderation ("warn first, then mute" — unconditional)
26
+ ## Environment Variables
27
+ ## Current model capabilities
28
+ ## Memory (MEMORY.md, <active_episodes>, <members>, member notes)
29
+ ## Run budget (tools-capable models only)
30
+ ## Destructive Operations — Confirmation Required
31
+ ## Presenting tool results ("simple lists", "never show JSON/commands")
32
+ ## Character (mrctl character flow + "standing instruction → set a preference")
33
+ reply-anchor sentence (swipe-replies only)
34
+ 3. MERCURY_EXT_SYSTEM_PROMPT ............... host runtime.ts ~1700-1760
35
+ a. extension before_container `systemPrompt` fragments
36
+ b. applicative profile `profile_prompt` (.mercury/active-profile.json)
37
+ c. "## Bot Character (set by the owner — applies to all conversations)"
38
+ (project_config.character)
39
+ d. space `system_prompt` (space_config, dashboard-set)
40
+ 4. <project_context> ....................... pi resource-loader.js loadProjectContextFiles
41
+ <project_instructions path="/home/mercury/.pi/agent/AGENTS.md"> ← .mercury/global/AGENTS.md
42
+ <project_instructions path="/spaces/<space>/AGENTS.md"> ← the space AGENTS.md
43
+ (pi walks from cwd to /; only the space dir is mounted, so only that
44
+ file and the global one exist. AGENTS.override.md / CLAUDE.md are
45
+ accepted names too; AGENTS.md is the convention.)
46
+ 5. <available_skills> ...................... pi skills.js formatSkillsForPrompt
47
+ name + description + SKILL.md path for every skill under
48
+ /home/mercury/.pi/agent/skills/** ← .mercury/global/skills/ (extension + built-in skills)
49
+ /spaces/<space>/.pi/skills/** ← space-local skills
50
+ 6. "Current working directory: /spaces/<space>"
51
+ ```
52
+
53
+ Two consequences worth reading twice:
54
+
55
+ - **Layers 1–3 are prose the model treats as equally authoritative.** Nothing
56
+ ranks "space AGENTS.md" above "## Moderation". When two of them speak to the
57
+ same subject the outcome is a coin flip weighted towards whichever is more
58
+ specific and later — and the platform text itself says so: *"a per-space
59
+ rule that is meant to override a global one has to say so in words rather
60
+ than rely on being more specific."* The only reliable override is an
61
+ explicit sentence naming what it overrides, plus deleting the competing
62
+ rule wherever you control it.
63
+ - **Layer 2 is unconditional.** Every space, whatever its profile, receives
64
+ the moderation paragraph, the "set a preference" paragraph and the
65
+ "simple lists, never raw output" paragraph. A persona that contradicts them
66
+ (a group where banter is the norm, a reporter who must not offer lists) has
67
+ to say so in its `AGENTS.md`. Removing the paragraphs per space is planned
68
+ (`docs/backlog/one-voice-per-space.md`); until then, override in words.
69
+
70
+ ## 2. The user prompt, top to bottom
71
+
72
+ Built by `buildPrompt()` in `container-entry.ts`:
73
+
74
+ ```
75
+ <caller id name role space/>
76
+ <run_budget>…</run_budget> wall clock for this run
77
+ <episodic_memory>…</episodic_memory> /spaces/<space>/MEMORY.md, verbatim, if present
78
+ <active_episodes>…</active_episodes> knowledge/episodes/*.md, keyword-scored (see §5)
79
+ <history>…</history> sliding window of stored turns (see §4)
80
+ <ambient_messages>…</ambient_messages> overheard group messages (ambient.enabled)
81
+ <members>…</members> space_roles — authoritative id→name→role
82
+ <member_notes>…</member_notes> knowledge/members/<id>.md, ≤6000 chars
83
+ <preferences>…</preferences> space_preferences rows
84
+ <attachments>…</attachments>
85
+ <reply_anchor>…</reply_anchor> swipe-replies only
86
+ <the prompt> chat message, or the task prompt, plus any
87
+ before_container promptAppend (e.g. a digest)
88
+ ```
89
+
90
+ A scheduled task uses exactly the same assembly; its prompt row is "the
91
+ prompt", and it runs as the caller who created it.
92
+
93
+ ## 3. Ownership, surface, and when a change lands
94
+
95
+ | Layer | Owner | Set from | Takes effect |
96
+ |---|---|---|---|
97
+ | pi preamble, Mercury platform block | mercury (code) | `src/agent/container-entry.ts` | **agent image rebuild** (`container/build.sh`), not a restart |
98
+ | `profile_prompt`, `member_permissions` | deploy (applicative profile) | `mercury profiles apply <dir>` → `.mercury/active-profile.json` | **restart** (read at startup) |
99
+ | Bot Character | bot owner (global admin) | `mrctl character set --file`, `PUT /api/character`, dashboard | next run |
100
+ | space `system_prompt` | space admin | dashboard Spaces settings (`space_config.system_prompt`) | next run |
101
+ | global `AGENTS.md` | deploy | `.mercury/global/AGENTS.md` (written by `mercury init` from `resources/templates/AGENTS.md`, overwritten by an applicative profile's `agents_md`) | next run (mounted `:ro`) |
102
+ | space `AGENTS.md` | space profile / operator | `.mercury/spaces/<space>/AGENTS.md`; `scripts/space-profile.ts apply` | next run (mounted per run) |
103
+ | space preferences | space admin (`prefs.set`) | `mrctl prefs set/delete`; ≤500 chars each, ≤50 per space | next run |
104
+ | space config (`trigger.*`, `context.*`, `ambient.enabled`, `role.<r>.permissions`, `<ext>.<key>`, `model.active`, `system_prompt`) | space admin / dashboard / profile | `mrctl config set`, dashboard, `space-profile apply` (`extension_config`) | next run (per-run read) |
105
+ | task prompts | whoever owns the `tasks` row | `mrctl tasks create`, dashboard, `space-profile apply` (`tasks:`) | next scheduled run |
106
+ | `MEMORY.md`, `knowledge/**` | the agent (and napkin's jobs) | `write` tool in-container; host edits | next run |
107
+ | extension skills | extension | `mercury.skill("./skill")` → copied to `.mercury/global/skills/<name>/` at startup | **restart** |
108
+ | space-local skills | operator / profile | `.mercury/spaces/<space>/.pi/skills/<name>/SKILL.md` | next run |
109
+ | `mercury.yaml` (`model.chain`, `context.window_size` default, `agent.trace_runs`, `extensions:` defaults) | operator | file + restart; `MERCURY_*` env wins over YAML | restart (some keys, e.g. `model.active`, are per-run) |
110
+
111
+ "Next run" means the running bot does not need a restart. Two traps:
112
+
113
+ - A restart never redeploys anything in layer 1 — `container-entry.ts` is
114
+ baked into the image. Verify by grepping the image
115
+ (`docker run --rm --entrypoint grep <image> -c "<marker>" /path`), never
116
+ the repo.
117
+ - `MERCURY_*` env overrides `mercury.yaml`; a YAML edit can silently do
118
+ nothing.
119
+
120
+ ## 4. The history window
121
+
122
+ - `context.mode` is a per-space key. `main` and DM auto-spaces are seeded
123
+ `context`; **any other space defaults to `clear`** (no history at all — only
124
+ the reply chain when someone swipe-replies). Set it explicitly.
125
+ - `context.window_size` (default 10 — `config.ts contextWindowSize`) counts
126
+ **user turns**: `getRecentTurns` takes the newest `turnCount*5` non-ambient
127
+ rows after the compact boundary and cuts at the Nth user row. Assistant rows
128
+ ride along. Scheduled runs are user rows too, so a space with six
129
+ scheduled runs a day spends most of its window on procedure text.
130
+ - A swipe-reply halves the window (`floor(window/2)`) and prepends the
131
+ replied-to chain (`context.reply_chain_depth`).
132
+ - `mrctl compact` moves the boundary permanently; `mrctl clear` clears the
133
+ next run only; `mrctl recall "<words>"` is a substring search over the
134
+ whole stored history for anything outside the window.
135
+ - Ambient messages have their own budget (30 rows) and do not compete with
136
+ turns.
137
+
138
+ The window is a *recent-conversation* mechanism, not a memory. Anything the
139
+ bot must get right a week later belongs in a file (§5), not in the hope that
140
+ it is still inside the window.
141
+
142
+ ## 5. Memory surfaces (what is injected, when)
143
+
144
+ | Surface | File | Injected | Condition |
145
+ |---|---|---|---|
146
+ | Episodic memory | `MEMORY.md` in the space dir | every run, verbatim, as `<episodic_memory>` | file exists and is non-empty. No size cap is enforced — the prompt asks for ~1500 tokens |
147
+ | Active episodes | `knowledge/episodes/*.md` | per run, as `<active_episodes>`, ≤800 tokens | frontmatter `status: active|cooling`, `keywords: ["…"]` (JSON array); score = keyword overlap × (active 1.0 / cooling 0.5) × log(mentions+1); body = `summary:` + `## Current State` |
148
+ | Member notes | `knowledge/members/<id>.md` | per run, as `<member_notes>` | `<id>` = platform id with `/`→`_`; ≤6000 chars total |
149
+ | Members | `space_roles` table | per run, as `<members>` | always; authoritative identity |
150
+ | Preferences | `space_preferences` | per run, as `<preferences>` | always |
151
+ | Vault entities | `knowledge/{people,projects,references,daily}/` | **not injected** | the agent must `read`/`napkin search` them; napkin's distillation job writes them |
152
+ | Anything else in the workspace | `knowledge/*.md`, `outbox/`, … | **not injected** | the agent reads it only if a rule tells it to (e.g. the football profile's `knowledge/push-log.md`) |
153
+
154
+ So: the only *always-on* memories are `MEMORY.md`, preferences and member
155
+ notes. Episodes need keywords that will actually appear in future messages.
156
+ Everything else is opt-in by instruction.
157
+
158
+ ## 6. Which layer for which kind of rule
159
+
160
+ | Kind of rule | Put it in | Not in |
161
+ |---|---|---|
162
+ | Who the bot is in this space, register, what it covers, sourcing and accuracy standard, formats, when it stays silent | space `AGENTS.md` | preferences (500-char blocks in the user prompt, no structure, easy to contradict) |
163
+ | A procedure: when it runs, what it produces, where it goes | the task prompt (`tasks/*.md` in a space profile) | `AGENTS.md` (a task prompt never repeats an editorial rule; `AGENTS.md` never describes a schedule) |
164
+ | A one-line behavioural setting a space admin may change from chat (`language`, `timezone`) | preference | `AGENTS.md` (would need a deploy to change) |
165
+ | Voice that must hold across all spaces of one deployment | Bot Character | every space's `AGENTS.md` (drifts) |
166
+ | Deterministic business logic, credentials, anything with numbers or dates | a capability / engine / extension, returning structured facts | prompt prose ("compute the day name yourself") |
167
+ | What members may do | `role.member.permissions` / applicative `member_permissions` | prompt prose ("only admins may…") — the model is not the enforcement |
168
+ | Knobs an extension reads (`feed-watch.*`, `longview.*`) | `extension_config` in the space profile (repo-owned, drift-checked) | hand edits on the box |
169
+ | A fact the bot keeps getting wrong | a file the bot reads each turn (`MEMORY.md`, a notebook under `knowledge/`), written with source and date | a longer rule |
170
+
171
+ One rule in exactly one layer. A rule repeated "for emphasis" is the next
172
+ contradiction — and when you move a rule into `AGENTS.md`, delete the
173
+ preference it came from (`remove_preferences:` in a space profile exists for
174
+ this).
175
+
176
+ ## 7. Reading the live layers
177
+
178
+ ```bash
179
+ # Everything the football space receives, without touching it
180
+ bun scripts/space-profile.ts dump --project ~/whatsapp-bot --space football-friends
181
+
182
+ # Drift between the repo profile and the live space
183
+ bun scripts/space-profile.ts check --project ~/whatsapp-bot --profile examples/profiles/football-reporter
184
+
185
+ # The exact prompt a run received (requires agent.trace_runs: true + image rebuild)
186
+ ls .mercury/traces/ # <container-name>.jsonl, 7-day TTL
187
+ ```
188
+
189
+ `agent.trace_runs` is off by default and needs the image rebuilt when
190
+ toggled. With it off, "what did the model actually see and open?" is
191
+ unanswerable after the fact — turn it on for a bounded window whenever you
192
+ are tuning a profile. A trace holds the full prompt and reply in clear text;
193
+ treat it like debug logging.
@@ -121,6 +121,8 @@ The directory must contain a `SKILL.md` file in pi's [skill format](https://agen
121
121
 
122
122
  Skills can contain multiple files — scripts, references, assets — not just SKILL.md. The agent uses relative paths from SKILL.md to access them.
123
123
 
124
+ Only the `name`, `description` and path of each skill reach the system prompt; the agent reads the body on demand when the description matches. How to write a description that fires, where space-local skills must live (`.mercury/spaces/<space>/.pi/skills/`), and how to make a profile actually use its skills: [skills-guide.md](skills-guide.md).
125
+
124
126
  ### `mercury.requires(capabilities)`
125
127
 
126
128
  Declare that this extension's skill (or CLI workflows) needs certain model capabilities (`tools`, `vision`, etc.). If **no** leg in `MERCURY_MODEL_CHAIN` satisfies **all** listed flags, the extension skill is not copied into the global skills directory and Mercury logs a startup warning.