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.
- {sessionmail-0.3.3 → sessionmail-0.4.0}/.claude-plugin/plugin.json +1 -1
- sessionmail-0.4.0/PKG-INFO +462 -0
- sessionmail-0.4.0/README.md +443 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/hooks/mail-guard/README.md +2 -2
- {sessionmail-0.3.3 → sessionmail-0.4.0}/pyproject.toml +4 -4
- {sessionmail-0.3.3 → sessionmail-0.4.0}/scripts/seat-agent/README.md +4 -2
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_birth.py +6 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_boxes.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_drop.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_fresh.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_generator.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_headers.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_host.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_index.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_init.py +2 -2
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_injectlog.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_launchd.py +77 -10
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_layer.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_maildir.py +107 -8
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_mcp.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_migrate.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_notmuch.py +7 -3
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_operator.py +138 -0
- sessionmail-0.4.0/src/sessionmail/_outside.py +126 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_parsers.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_pull_agent.py +64 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_pull_imap.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_reach.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_record.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_render.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_root.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_say.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_seat.py +94 -24
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_send.py +2 -2
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_sender.py +107 -25
- sessionmail-0.4.0/src/sessionmail/_service_file.py +210 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_settings.py +44 -4
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_setup.py +406 -43
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_shorten.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_stub.py +10 -7
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_subscribe.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_subscription.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_views.py +27 -6
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_voice.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_why.py +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/hook_lines.toml +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mail.schema.json +2 -2
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/memory.py +4 -3
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/message.py +208 -45
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-wake +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/notmuch-config.sessionkit.template +4 -2
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/post-new +1 -1
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/pager.py +61 -11
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/vmailbox-gen +2 -2
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/rules.py +12 -5
- sessionmail-0.3.3/PKG-INFO +0 -252
- sessionmail-0.3.3/README.md +0 -233
- {sessionmail-0.3.3 → sessionmail-0.4.0}/.gitignore +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/LICENSE +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/plugins/pager-monitor/README.md +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/__init__.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_archive.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_arm.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_detect.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_detect_imap.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_frozen.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_help.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_magicdns.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_metrics.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_mhs.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_puller.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_sig.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/_tokens.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/arm.txt +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/humans +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/mail-box.lib.sh +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/mail-ext.lib.sh +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/mhs-thread +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-cat +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-ls +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-run +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-tree +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/mhs/session-mail-verify +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/notmuch-config.store.template +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/notmuch/store-index +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/delivery-check +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/magicdns-suffix +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/postfix/nm-deliver +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/seat/seat-agent +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/seat/seat-agent.plist.template +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/seat_init.py +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/src/sessionmail/settings.template.json +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/tests/fixtures/sig_v2/README.md +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/tests/fixtures/sm827_hint_root/README.md +0 -0
- {sessionmail-0.3.3 → sessionmail-0.4.0}/tests/fixtures/sm827_honest/README.md +0 -0
|
@@ -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
|