sessionmail 0.3.3__tar.gz → 0.4.0__tar.gz

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 (95) hide show
  1. {sessionmail-0.3.3 → sessionmail-0.4.0}/.claude-plugin/plugin.json +1 -1
  2. sessionmail-0.4.0/PKG-INFO +462 -0
  3. sessionmail-0.4.0/README.md +443 -0
  4. {sessionmail-0.3.3 → sessionmail-0.4.0}/hooks/mail-guard/README.md +2 -2
  5. {sessionmail-0.3.3 → sessionmail-0.4.0}/pyproject.toml +4 -4
  6. {sessionmail-0.3.3 → sessionmail-0.4.0}/scripts/seat-agent/README.md +4 -2
  7. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_birth.py +6 -0
  8. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_boxes.py +1 -1
  9. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_drop.py +1 -1
  10. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_fresh.py +1 -1
  11. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_generator.py +1 -1
  12. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_headers.py +1 -1
  13. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_host.py +1 -1
  14. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_index.py +1 -1
  15. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_init.py +2 -2
  16. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_injectlog.py +1 -1
  17. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_launchd.py +77 -10
  18. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_layer.py +1 -1
  19. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_maildir.py +107 -8
  20. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_mcp.py +1 -1
  21. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_migrate.py +1 -1
  22. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_notmuch.py +7 -3
  23. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_operator.py +138 -0
  24. sessionmail-0.4.0/src/sessionmail/_outside.py +126 -0
  25. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_parsers.py +1 -1
  26. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_pull_agent.py +64 -1
  27. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_pull_imap.py +1 -1
  28. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_reach.py +1 -1
  29. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_record.py +1 -1
  30. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_render.py +1 -1
  31. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_root.py +1 -1
  32. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_say.py +1 -1
  33. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_seat.py +94 -24
  34. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_send.py +2 -2
  35. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_sender.py +107 -25
  36. sessionmail-0.4.0/src/sessionmail/_service_file.py +210 -0
  37. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_settings.py +44 -4
  38. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_setup.py +406 -43
  39. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_shorten.py +1 -1
  40. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_stub.py +10 -7
  41. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_subscribe.py +1 -1
  42. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_subscription.py +1 -1
  43. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_views.py +27 -6
  44. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_voice.py +1 -1
  45. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_why.py +1 -1
  46. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/hook_lines.toml +1 -1
  47. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mail.schema.json +2 -2
  48. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/memory.py +4 -3
  49. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/message.py +208 -45
  50. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-wake +1 -1
  51. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/notmuch-config.sessionkit.template +4 -2
  52. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/post-new +1 -1
  53. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/pager.py +61 -11
  54. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/vmailbox-gen +2 -2
  55. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/rules.py +12 -5
  56. sessionmail-0.3.3/PKG-INFO +0 -252
  57. sessionmail-0.3.3/README.md +0 -233
  58. {sessionmail-0.3.3 → sessionmail-0.4.0}/.gitignore +0 -0
  59. {sessionmail-0.3.3 → sessionmail-0.4.0}/LICENSE +0 -0
  60. {sessionmail-0.3.3 → sessionmail-0.4.0}/plugins/pager-monitor/README.md +0 -0
  61. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/__init__.py +0 -0
  62. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_archive.py +0 -0
  63. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_arm.py +0 -0
  64. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_detect.py +0 -0
  65. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_detect_imap.py +0 -0
  66. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_frozen.py +0 -0
  67. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_help.py +0 -0
  68. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_magicdns.py +0 -0
  69. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_metrics.py +0 -0
  70. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_mhs.py +0 -0
  71. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_puller.py +0 -0
  72. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_sig.py +0 -0
  73. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_tokens.py +0 -0
  74. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/arm.txt +0 -0
  75. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/humans +0 -0
  76. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/mail-box.lib.sh +0 -0
  77. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/mail-ext.lib.sh +0 -0
  78. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/mhs-thread +0 -0
  79. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-cat +0 -0
  80. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-ls +0 -0
  81. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-run +0 -0
  82. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-tree +0 -0
  83. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-verify +0 -0
  84. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/notmuch-config.store.template +0 -0
  85. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/store-index +0 -0
  86. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/delivery-check +0 -0
  87. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/magicdns-suffix +0 -0
  88. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/nm-deliver +0 -0
  89. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/seat/seat-agent +0 -0
  90. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/seat/seat-agent.plist.template +0 -0
  91. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/seat_init.py +0 -0
  92. {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/settings.template.json +0 -0
  93. {sessionmail-0.3.3 → sessionmail-0.4.0}/tests/fixtures/sig_v2/README.md +0 -0
  94. {sessionmail-0.3.3 → sessionmail-0.4.0}/tests/fixtures/sm827_hint_root/README.md +0 -0
  95. {sessionmail-0.3.3 → sessionmail-0.4.0}/tests/fixtures/sm827_honest/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sessionmail",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "Mail for coding agents: message moves messages, pager alerts, memory is the filesystem view - three commands over one maildir.",
5
5
  "author": {
6
6
  "name": "Mike Bailey",
@@ -0,0 +1,462 @@
1
+ Metadata-Version: 2.5
2
+ Name: sessionmail
3
+ Version: 0.4.0
4
+ Summary: sessionmail - mail for coding agents: message, pager and memory over one maildir.
5
+ Project-URL: Homepage, https://github.com/sessionkit/sessionmail
6
+ Project-URL: Repository, https://github.com/sessionkit/sessionmail
7
+ Author-email: Mike Bailey <mike@sessionkit.ai>
8
+ License-Expression: AGPL-3.0-or-later
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Requires-Python: <3.15,>=3.11
12
+ Requires-Dist: click>=8.1
13
+ Requires-Dist: cryptography>=42
14
+ Requires-Dist: markdown>=3.6
15
+ Requires-Dist: mcp>=2.2.0
16
+ Requires-Dist: pymdown-extensions>=10.0
17
+ Requires-Dist: sessiontools>=0.3.3
18
+ Description-Content-Type: text/markdown
19
+
20
+ # sessionmail
21
+
22
+ Mail for coding agents: `message` moves messages, `pager` alerts, `memory`
23
+ is the filesystem view - three commands over one maildir.
24
+
25
+ The idea behind it: [The Mailbox Is the Memory][paper], a paper written on
26
+ the mail it describes.
27
+
28
+ [paper]: https://cora7.com/blog/the-mailbox-is-the-memory/
29
+
30
+ ## Quick start
31
+
32
+ ```sh
33
+ claude plugin marketplace add sessionkit/sessionmail
34
+ claude plugin install sessionmail
35
+ uv tool install sessionmail
36
+ pager listen
37
+ ```
38
+
39
+ Both of the first two lines need the repo and the release to be public:
40
+ see **Status**, under Installation, before you copy them. The third
41
+ installs the commands the plugin calls.
42
+
43
+ The last line is the point: `pager listen` blocks until mail arrives, then
44
+ says so. An agent that runs it in the background can be reached while it
45
+ is idle, instead of finding out on its next manual check. Under an
46
+ interactive Claude Code seat the plugin does it for you: its
47
+ **pager monitor** listens from launch, on by default (see
48
+ [the pager monitor][pager-monitor]). The hand `pager listen` is for long-lived
49
+ headless (`claude -p`) seats, seats where the monitor is switched off, and
50
+ clients outside Claude Code. A one-shot `claude -p` arms nothing, and the
51
+ hooks never ask it to.
52
+
53
+ ```sh
54
+ message send NAME -s SUBJECT -f BODY.md
55
+ pager listen
56
+ pager status
57
+ memory ls /
58
+ memory cat PATH
59
+ ```
60
+
61
+ - `message send` sends a message; `message` also has `list`, `tree`,
62
+ `read` and `mark`.
63
+ - `pager listen` blocks until mail arrives, pages, and exits.
64
+ - `pager status` says what would page you now.
65
+ - `memory ls` and `memory cat` read the box as a filesystem.
66
+
67
+ To check that mail moves, send a box a message and list that same box:
68
+
69
+ ```sh
70
+ message setup --box claude
71
+ message send claude -s hi --no-body
72
+ message list claude
73
+ ```
74
+
75
+ The first line makes the host ready, and a box named `claude` to test
76
+ with. The second sends that box a message, and the third lists the box
77
+ you sent to: `hi` is there.
78
+
79
+ Name the box in `message list` too. With no box, `message list` reads
80
+ this session's box once it exists, else the seat's box (stderr says so if
81
+ the session has none yet). Under Claude Code the plugin makes the session
82
+ box when the session starts, so a bare `message list` there does not show
83
+ `hi`.
84
+
85
+ Each command's own `--help` is the reference; `<command> <verb> --help` is
86
+ the page for a verb.
87
+
88
+ ## Requirements
89
+
90
+ - [x] macOS (tested on macOS 27, Apple silicon)
91
+ - [ ] Linux
92
+ - [ ] WSL
93
+ - [ ] Windows
94
+
95
+ What sessionmail depends on, what needs each one, and how to upgrade it:
96
+ [dependencies][dependencies].
97
+
98
+ ## Installation
99
+
100
+ **Two installs, and you need both.** The plugin brings the skills, the
101
+ hooks, the pager monitor and the MCP servers; the package brings `message`,
102
+ `pager` and `memory`, which is what all of those call.
103
+
104
+ ```sh
105
+ claude plugin install sessionmail
106
+ uv tool install sessionmail
107
+ ```
108
+
109
+ The first line brings the skills, the hooks and the MCP entries; the
110
+ second puts the three commands on your PATH.
111
+
112
+ On Claude Code 2.1.275 or later the plugin installs from its marketplace in
113
+ one step, from inside a session:
114
+
115
+ ```text
116
+ /plugin install sessionmail --marketplace sessionkit/sessionmail
117
+ ```
118
+
119
+ With only the plugin, the hooks exit quietly and the MCP servers do not
120
+ start: you get skills describing commands the machine does not have. The
121
+ hook says so once per session rather than leaving you to guess.
122
+
123
+ **Same-host mail needs no mail server.**
124
+
125
+ - `message send` writes each copy straight into this host's maildirs: [the
126
+ file drop][file-drop], the default. Mail to another host is refused by
127
+ name.
128
+ - Reading needs no server either: `message list`, `tree` and `read`, and
129
+ `pager`, read the maildir directly. `message mark` and `memory` read the
130
+ [notmuch][] index, and refresh it themselves before they read (below).
131
+ - **Mail across machines** goes through postfix, which is opt-in:
132
+ [postfix configuration][postfix-configuration].
133
+
134
+ **Then make the host ready** with `message setup`: [message
135
+ setup][message-setup].
136
+
137
+ ```sh
138
+ message setup --dry-run
139
+ message setup --box NAME
140
+ ```
141
+
142
+ The first line prints the plan and changes nothing. The second applies
143
+ it: the mail root, the boxes, the notmuch profile and its hook.
144
+
145
+ - It makes the mail root, the operator's box and each `--box NAME` (the
146
+ file drop never makes a box), and sessionmail's own notmuch profile. It
147
+ links notmuch's [post-new hook][post-new-hook] into that profile's hook
148
+ folder. It prints its whole plan before it applies any of it, and it is
149
+ safe to re-run.
150
+ - It never touches your own notmuch config and never edits postfix. It
151
+ writes one settings key, `host`, and only into a settings file that has
152
+ none.
153
+ - **Setup runs `notmuch new --no-hooks` once**, on its own empty profile,
154
+ to build the index the hook needs. It schedules nothing and installs no
155
+ job.
156
+ - **The readers keep the index fresh.** `memory` runs a bounded
157
+ `notmuch new --no-hooks` before it reads, and `message mark` runs one
158
+ when an id is missing. The bound is `notmuch.refresh`: 2 seconds by
159
+ default, and 0 turns it off: [notmuch][].
160
+
161
+ `message`, `pager` and `memory` all run; the specs, in the git repository
162
+ (the release archive leaves them out), say what each verb is meant to do
163
+ and `make progress` counts how much of it is built.
164
+
165
+ ### What an install changes
166
+
167
+ `uv tool install sessionmail` changes two places and fills uv's own
168
+ cache. `claude plugin install` changes Claude Code's own files. Neither
169
+ touches a shell startup file. Everything else below appears when you run
170
+ `message setup`, when a Claude Code session starts with the plugin, or only
171
+ when you run the verb named beside it. On a fresh host, every file
172
+ sessionmail keeps is under `~/.sessionkit/mail/`, apart from the mail state
173
+ in each session's folder. Each line says what puts it there, and
174
+ [Uninstall][uninstall] has the lines that take each one away.
175
+
176
+ - **PATH:** `message`, `pager` and `memory` in `~/.local/bin/`, or in
177
+ `UV_TOOL_BIN_DIR` when that is set: `uv tool install sessionmail`. uv
178
+ says so if the folder is not on your PATH, and edits no shell file.
179
+ - **The program:** `~/.local/share/uv/tools/sessionmail/`, or in
180
+ `UV_TOOL_DIR`: its own Python environment, with the sessiontools library
181
+ in it but not its `session` command. The notmuch hook links below point
182
+ into it.
183
+ - **uv's cache:** what the install downloaded and built. It is uv's,
184
+ shared by everything uv installs. `uv cache dir` prints where it is.
185
+ - **The plugin:** `claude plugin marketplace add` and `claude plugin
186
+ install`. Claude Code names the marketplace and the plugin in
187
+ `~/.claude/settings.json` (`extraKnownMarketplaces`, `enabledPlugins`),
188
+ lists them in `~/.claude/plugins/`, and keeps a copy in
189
+ `~/.claude/plugins/cache/sessionmail/`. The plugin brings:
190
+ - three skills: `message`, `pager` and `memory`;
191
+ - three MCP servers of the same names, each running `<command> mcp`;
192
+ - six hook entries: SessionStart, Stop, PostToolBatch and
193
+ UserPromptSubmit run `pager hook`, and two PreToolUse entries, one for
194
+ Bash and one for the mail tools, run the mail guard;
195
+ - one monitor, `pager`, which runs `pager listen --monitor` in every
196
+ interactive session: [the pager monitor][pager-monitor].
197
+ - **Services (launchd agents):** none until you install one. Each writes a
198
+ plist in `~/Library/LaunchAgents/` and loads it, and the job starts
199
+ again at every login.
200
+ - `dev.sessionkit.mail.seat-agent.<ADDRESS>`: `message seat agent
201
+ --install ADDRESS`, the ssh-agent of a seat with no Claude session
202
+ ([`message seat`][message-seat]).
203
+ - `dev.sessionkit.mail.puller`, or the label in the `pull.label` key:
204
+ `message setup --pull` ([the pull][pull]).
205
+ - **The notmuch hook:** two links, `post-new` and `post-insert`, to the
206
+ packaged hook, in the hook folder notmuch reports for sessionmail's
207
+ profile (`database.hook_dir`, inside the index): `message setup`.
208
+ - **The notmuch profile:**
209
+ `~/.sessionkit/mail/settings/notmuch/sessionkit/config`: `message setup`,
210
+ only when there is none. A host that already has
211
+ `~/.config/notmuch/sessionkit/config` or `~/.notmuch-config.sessionkit`
212
+ keeps that file, and setup writes no other. `message path
213
+ --notmuch-config` names the one in use. Your own `~/.notmuch-config` is
214
+ never read or written.
215
+ - **The mail root:** `~/.sessionkit/mail/` (`message path --root`). A host
216
+ whose `root` key or `SESSIONMAIL_ROOT` names another folder keeps its
217
+ mail there.
218
+ - `stores/`, made 0700, with the operator's box and each `--box NAME`
219
+ in it: `message setup`.
220
+ - `stores/<session-id>/`, one box for each Claude Code session: the
221
+ SessionStart hook, or `pager listen`. Neither makes the mail root
222
+ itself.
223
+ - `.notmuch/`, sessionmail's index: `message setup` builds it once, with
224
+ `notmuch new --no-hooks`. `memory` and `message mark` keep it fresh.
225
+ - `archive/`: `message box archive --apply`, which moves dead session
226
+ boxes there.
227
+ - **Settings:** in `~/.sessionkit/mail/settings/`.
228
+ - `mail.schema.json` and `mail.template.json`: the SessionStart hook, or
229
+ `pager settings install`.
230
+ - `mail.json`: the first PostToolBatch hook writes it from the
231
+ template, with the hooks off, and says so once. `pager settings hooks
232
+ on` turns them on. `message setup` adds `host` to a file that has none.
233
+ - **State:** in `~/.sessionkit/mail/state/`, each folder made by a verb:
234
+ `seat-keys/` (`message seat init`), `seats/` (`message mark` from a seat
235
+ with no session), `puller/` and `imap/` (the pull), and `post-new/` (only
236
+ once you set [the post-new command][post-new-command]).
237
+ - **Logs:** in `~/.sessionkit/mail/log/`: `settings.jsonl` (`pager settings
238
+ hooks`), `seat-agent.<ADDRESS>.log` and `puller.log`. [Logs][logs] has
239
+ each one.
240
+ - **In each session's folder:** sessiontools makes the folder,
241
+ `~/.sessionkit/tools/state/sessions/<session-id>/` on a fresh host, or
242
+ `~/.session/sessions/<session-id>/` on a host that still has that.
243
+ sessionmail adds `mail/`, the session's mail state, and `log/mail/`, the
244
+ pager's log.
245
+ - **Only on a postfix host** ([postfix configuration][postfix-configuration]):
246
+ the maps the generator writes, as you, in `/usr/local/etc/postfix-agents/`
247
+ (a folder you make once with `sudo`); the delivery wrapper `nm-deliver`
248
+ beside them, on a host whose postfix runs an `nminsert` service (`message
249
+ setup`, with `sudo`, keeping its copy in
250
+ `~/.sessionkit/mail/settings/postfix/`); and the generator's
251
+ `<mail root>/.state/` folder. sessionmail never edits `main.cf` or
252
+ `master.cf`.
253
+ - **Nothing else:** no shell startup file, no crontab, no `~/.mail` on a
254
+ fresh host, and nothing in your own notmuch or postfix configuration.
255
+
256
+ ## Uninstall
257
+
258
+ Each item in [what an install changes][install-changes] has its lines
259
+ here. Skip the lines for what you never installed. Quit every `claude`
260
+ session first, and paste the lines in one terminal, in this order: the
261
+ first lines need the `message` command, which the tool's line removes.
262
+ The lines carry no comments, because in zsh a `#` typed at the prompt is
263
+ not a comment.
264
+
265
+ **Before you start**, note where the mail root and the notmuch profile
266
+ are. The later lines read these two names:
267
+
268
+ ```sh
269
+ R="$(message path --root)"
270
+ P="$(message path --notmuch-config)"
271
+ ```
272
+
273
+ **The services**, if you installed one. Put the seat's address for
274
+ `ADDRESS`, and your own label if you set `pull.label`:
275
+
276
+ ```sh
277
+ message seat agent --uninstall ADDRESS
278
+ launchctl bootout "gui/$(id -u)/dev.sessionkit.mail.puller"
279
+ rm ~/Library/LaunchAgents/dev.sessionkit.mail.puller.plist
280
+ ```
281
+
282
+ **The notmuch hook and profile.** The first line turns off [the post-new
283
+ command][post-new-command], which notmuch also keeps in its index. The
284
+ second finds the hook folder. The third removes each hook only when it is
285
+ a link to sessionmail's hook, so a hook of your own there stays. The last
286
+ removes the profile. If `P` is empty there is no profile: skip these
287
+ lines. Each one stops, doing nothing, when `P` is empty, because notmuch
288
+ given no profile answers for your own database. The notmuch lines run
289
+ without `NOTMUCH_CONFIG`, `NOTMUCH_DATABASE` and `NOTMUCH_PROFILE`, as
290
+ setup does, because each of them would point notmuch at another database:
291
+
292
+ ```sh
293
+ env -u NOTMUCH_CONFIG -u NOTMUCH_DATABASE -u NOTMUCH_PROFILE notmuch --config="${P:?}" config set sessionmail.post_new_command
294
+ H="$(env -u NOTMUCH_CONFIG -u NOTMUCH_DATABASE -u NOTMUCH_PROFILE notmuch --config="${P:?}" config get database.hook_dir)"
295
+ for f in "${H:?}/post-new" "${H:?}/post-insert"; do case "$(readlink "$f")" in */sessionmail/notmuch/post-new|*/hooks/notmuch-post-new) rm "$f" ;; esac; done
296
+ rm "${P:?}"
297
+ ```
298
+
299
+ **The plugin.** The first line removes the plugin, and the second removes
300
+ its marketplace. The third deletes the copy Claude Code keeps, which the
301
+ first two leave:
302
+
303
+ ```sh
304
+ claude plugin uninstall sessionmail@sessionmail
305
+ claude plugin marketplace remove sessionmail
306
+ rm -rf "${HOME:?}/.claude/plugins/cache/sessionmail"
307
+ ```
308
+
309
+ **The commands and the program:**
310
+
311
+ ```sh
312
+ uv tool uninstall sessionmail
313
+ ```
314
+
315
+ uv's cache stays, with the other things uv installed. `uv cache clean
316
+ sessionmail` drops this package's entries.
317
+
318
+ **Your mail and sessionmail's files, only if you want them gone.** This
319
+ cannot be undone. The first line deletes the mail root: every box, the
320
+ index and the archive. The second deletes the settings, the state and the
321
+ logs. On a fresh host the mail root is the second folder, so the second
322
+ line finds nothing left:
323
+
324
+ ```sh
325
+ rm -rf "${R:?}"
326
+ rm -rf "${HOME:?}/.sessionkit/mail"
327
+ ```
328
+
329
+ The mail state in the session folders. In zsh, a line prints `no matches
330
+ found` when there is nothing to delete; that is fine:
331
+
332
+ ```sh
333
+ rm -rf "${HOME:?}"/.sessionkit/tools/state/sessions/*/mail
334
+ rm -rf "${HOME:?}"/.sessionkit/tools/state/sessions/*/log/mail
335
+ rm -rf "${HOME:?}"/.session/sessions/*/mail
336
+ rm -rf "${HOME:?}"/.session/sessions/*/log/mail
337
+ ```
338
+
339
+ **On a postfix host**, take the maps and the `nminsert` service out of
340
+ postfix's configuration first ([postfix
341
+ configuration][postfix-configuration]). Then delete the folder they were
342
+ in:
343
+
344
+ ```sh
345
+ sudo rm -rf /usr/local/etc/postfix-agents
346
+ ```
347
+
348
+ ## Configuration
349
+
350
+ Which files, which environment variables, and where each one lives, one
351
+ component at a time: [configuration][configuration].
352
+
353
+ - **sessionmail:** `~/.sessionkit/mail/settings/mail.json` for the host, and one for
354
+ a seat or a session if you want them. Its keys: [settings][settings].
355
+ - **postfix**, only to send: `/etc/postfix/main.cf`, root's and set once,
356
+ and the two maps the generator writes as you, in
357
+ `/usr/local/etc/postfix-agents/`.
358
+ - **notmuch**, the index: sessionmail's own profile,
359
+ `~/.sessionkit/mail/settings/notmuch/sessionkit/config` on a fresh host.
360
+ An older `~/.config/notmuch/sessionkit/config` (or
361
+ `~/.notmuch-config.sessionkit`) is still read while it exists, and is
362
+ never moved. Your own `~/.notmuch-config` is never read.
363
+ - **Environment variables**: each settings key has one (the `sendmail`
364
+ key's is `SESSIONMAIL_SENDMAIL`). When it is set and not empty, it wins
365
+ over the file.
366
+
367
+ ## Logs
368
+
369
+ sessionmail keeps four logs. Each is written only when it has something
370
+ to record:
371
+
372
+ - `~/.session/sessions/<session-id>/log/mail/pager.jsonl`: one row for
373
+ each thing the pager put into an agent's context, sizes and Message-IDs
374
+ only ([the pager log][pager-inject-log]). Past 16 MB it becomes
375
+ `pager.1.jsonl`.
376
+ - `~/.session/sessions/<session-id>/log/settings.jsonl`, and the same file
377
+ under `~/.session/seats/<seat>/`: each settings change for that session
378
+ or seat, such as a subscription or its address, with the command that
379
+ made it.
380
+ - `<mail root>/.state/regen-failures.log` (`message path --root`): each time the map generator
381
+ refused to rewrite the alias map, and why.
382
+ - `~/.sessionkit/mail/state/post-new/log` (`~/.sessionmail/post-new/log` on
383
+ an existing host where that folder is still real): the [post-new command][post-new-command]'s
384
+ runs and output, only once you set one. Past 1 MiB it becomes `log.1`.
385
+
386
+ Follow one as it grows with `tail -F`, for example
387
+ `tail -F "$(message path --root)/.state/regen-failures.log"`. `pager status` ends its
388
+ `today:` line with the path of this session's `pager.jsonl`.
389
+
390
+ - **Health:** `pager status`, run by the agent. `monitor: armed (pid N)`,
391
+ or a hand `pager listen` named on that line, means something is
392
+ listening for its mail; `not armed` or `off` alone means nothing is.
393
+ Exit 2 means it could not look (no session, or no box yet). It does not
394
+ test sending: `message send` exits 3 when the mail server did not take
395
+ the message.
396
+
397
+ ## Docs
398
+
399
+ Everything else is in `docs/`, one subject per file:
400
+
401
+ - A page for each command: [`message`][message-doc], [`memory`][memory-doc]
402
+ and [the pager][pager].
403
+ - [The pager][pager], the model in one page; [how `pager listen`
404
+ works][pager-listen-internals]; [the pager monitor][pager-monitor] and
405
+ its [frozen-turn watch][frozen-watch], off by default; [not running
406
+ under Claude Code][pager-outside-claude]; [what the pager
407
+ injects][pager-inject-log]; [outside mail, paged by
408
+ pointer][outside-pointer].
409
+ - Four hidden verbs: [`message seat`][message-seat], a seat with no Claude
410
+ session; [`message receipt`][message-receipt], what another session
411
+ recorded; [`message address`][message-address], the address this shell
412
+ sends from; and [`message setup`][message-setup], making a host ready.
413
+ - [Your first memory root, by hand][memory-first-root]: `memory` has no
414
+ automated first step yet.
415
+ - [Gating on `message verify --json`][verify-json-verdict]: the verdict as
416
+ one JSON object.
417
+ - [The writing model][session-model]: the signed `Session-Model:` header,
418
+ and where its id comes from for Claude Code and for another harness.
419
+ - Delivery: [where mail comes from][mail-delivery-paths], [postfix
420
+ configuration][postfix-configuration], [the file drop][file-drop],
421
+ [pulling a mail host's boxes][pull].
422
+ - [Configuration][configuration]: the files you set up, by component;
423
+ [settings][settings], the keys of `mail.json`.
424
+ - [Dependencies][dependencies]: what sessionmail needs, and how to upgrade
425
+ each one.
426
+ - Index: [notmuch][], [notmuch settings][notmuch-settings], [the post-new
427
+ command][post-new-command].
428
+ - [The plugin's three skills][plugin-skills]; [development][development-setup],
429
+ including `make test-latency`, the wall-clock tests `make test` leaves out.
430
+
431
+ [dependencies]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/dependencies.md
432
+ [pager]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/pager.md
433
+ [message-doc]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/message.md
434
+ [memory-doc]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/memory.md
435
+ [pager-listen-internals]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/pager-listen-internals.md
436
+ [pager-monitor]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/pager-monitor.md
437
+ [frozen-watch]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/frozen-watch.md
438
+ [pager-outside-claude]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/pager-outside-claude.md
439
+ [pager-inject-log]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/pager-inject-log.md
440
+ [outside-pointer]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/outside-pointer.md
441
+ [message-seat]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/message-seat.md
442
+ [session-model]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/session-model.md
443
+ [message-receipt]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/message-receipt.md
444
+ [message-address]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/message-address.md
445
+ [message-setup]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/message-setup.md
446
+ [memory-first-root]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/memory-first-root.md
447
+ [verify-json-verdict]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/verify-json-verdict.md
448
+ [mail-delivery-paths]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/mail-delivery-paths.md
449
+ [postfix-configuration]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/postfix-configuration.md
450
+ [configuration]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/configuration.md
451
+ [settings]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/settings.md
452
+ [file-drop]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/file-drop.md
453
+ [pull]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/pull.md
454
+ [notmuch]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/notmuch.md
455
+ [notmuch-settings]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/notmuch-settings.md
456
+ [post-new-hook]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/notmuch-settings.md#the-post-new-hook
457
+ [post-new-command]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/post-new-command.md
458
+ [plugin-skills]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/plugin-skills.md
459
+ [development-setup]: https://github.com/sessionkit/sessionmail/blob/HEAD/docs/development-setup.md
460
+ [uninstall]: #uninstall
461
+ [install-changes]: #what-an-install-changes
462
+ [logs]: #logs