Repository navigation
Hosted channels: the email channel - #601
alex-clickhouse wants to merge 7 commits into
Conversation
|
Nerve PR review · ✅ Ready to merge (after #603) Updated for dbad161: the PR is rewritten. Member mail is now an invocation channel instead of a source, as the author describes above. It is stacked on #603 and reviewed against that branch. Email becomes the second entry in #603's hosted provider table ( Completeness: a dedicated test file covers pinned contract samples, a real stream, the kept session and one session per thread, the switch, the frame, reload and doctor. Startup tests and docs (config, the reload table, architecture, the example config) are included. No migration. Checked: 26 related test files (hosted channel/email/behaviour/sources, contract, config reload/validate, gateway startup, drain, attribution, Slack channel) in an offline sandbox at |
Member mail arrives from the channel gateway as an email invocation that starts or continues a session. The generic provider behaviour does the rest: the email connection answers with a tool and keeps its sessions, so Nerve posts nothing and the agent answers with the reply tool. Email is an entry of the hosted chat provider table. Usable channels.hosted settings give the channel, also while the new email.enabled setting is false, and the switch is read per event, so a reload switches email on or off without a restart. While it is off, the channel refuses email events and the reader goes on. The gateway takes mail only for a subscribed agent, so a runtime with the email channel always sends a subscriptions frame: with the email entry while email is on, without it while email is off, also when no hosted source exists. A reload that switches email off then takes the entry away and the gateway refuses new mail. Without usable hosted settings, email.enabled gets a startup warning and no channel. The C2 event and the email subscriptions frame are copies of the contract samples, pinned by their SHA-256. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
email.enabled works only with usable channels.hosted settings, and the startup warning is easy to miss. The doctor shows the same warning, an error for a key file that cannot be read, and a warning while a switched-off hosted Slack holds member mail back. While email is switched off, the doctor output does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/config.md gets an Email section: the email.enabled setting, the need for usable channels.hosted settings, one session per sender and email thread that keeps its session, no posted answer and the reply tool, and how a reload switches email on or off through the subscriptions. The reload table, the hosted mode text, the connection behaviour section and the architecture overview name email as the second hosted chat provider. config.example.yaml shows the setting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
send_channel_message to the email channel was refused with "email.allow_outbound is not enabled", a key that does not exist, so the agent could tell its user to set it. A provider entry now names its outbound key (Slack: slack.allow_outbound); a provider without one is refused with "the <provider> channel does not post to a conversation that the agent names". The refusal itself and Slack's text do not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reload that switches email off must take the email entry away at the gateway. With no open stream, or none that sent the frame in time, the reload said "published on 0 stream(s)" and counted it as a success, while the gateway kept the old subscriptions. republish_subscriptions() now raises SubscriptionsNotSent in that case, and the reload reports subscriptions as an error that says they are not sent yet and that a stream that connects sends them before Nerve reads from it. Tests switch email off and on with no stream open, reconnect, and check that the new stream sends the current subscriptions before its first read. Mail that the gateway stores before it gets them is rejected when Nerve reads it; the docs say that this is the result of the switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reload that switched email on without usable channels.hosted settings said nothing: the warning came only at startup and in nerve doctor. A reload while email.enabled is true now repeats that warning under email. When the settings are usable but this process has no email channel, because they became usable after startup or the hosted runtime did not start, it says so under email and adds a line to restart_required. Nothing changes while email is off or its channel runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every stream that connects sends the current subscriptions before
Nerve reads from it, so the gateway always gets them. Reporting a
republish that no stream sent as an error made workspace sync run
the whole reload again on every cycle, and the setup page show a
warning after each save, while the gateway could not be reached.
The reload result keeps the same text ("not sent yet: no gateway
stream is open; ...", or how many open streams did not send them in
time), now as a note that leaves the reload ok.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1d87838 to
dbad161
Compare
|
@nerveai Thanks. That was not the intended behaviour. The PR is now rewritten:
The earlier cursor gap for hosted |
Stack (merge in this order): #603 generic hosted channel behaviour -> #601 the email channel. The channel gateway that sends the new capabilities members and the email events ships first.
What
email.enabled(defaultfalse). With usablechannels.hostedsettings, Nerve has an email channel: the channel gatewaydelivers the mail that members of the agent send to its address, and each
mail starts or continues a session.
email:<conversation ID>),kept however long it was idle. Nerve posts no answer: the agent answers
with the reply tool that the gateway names, and the turn's text starts
with a line that says so.
emailwhileemail.enabledistrue, sothe gateway takes member mail only then. A reload switches email on or off
without a restart. While it is off, Nerve rejects email events and goes on
reading the events of the other providers.
channels.hostedsettings,email.enabledgives a startupwarning, the same warning in
nerve doctor, and no channel.not sent yet; a stream that connects sends them before Nerve reads from
it. A reload while
email.enabledistruesays why there is no emailchannel, or that a restart is needed.
send_channel_messagewith a text that names no config key.nerve doctorreports the email channel. Docs: an Email section indocs/config.md, the reload table, the hosted mode text,docs/architecture.md, andconfig.example.yaml.How
channel exists whenever the hosted settings are usable, and its switch is
email.enabled, read for each event. It has no settings of its own andrefuses its events while it is off.
subscribes, makes the runtime add an entry withthe provider's name and empty filters to the subscriptions frame while the
provider is on. A runtime with such a provider sends the frame on every
stream, also without hosted sources, and again on each reload. Nerve checks
the email entry as the gateway does.
announces the email connection with
answer: tool, the reply tool, andsession: keep.channel contract's samples, pinned by their SHA-256.
Why
Member mail is an invocation, not a source record: a member writes to the
agent and expects the agent to act and to answer when it decides to. The
earlier source design for this PR is replaced; it never shipped.
Notes
subscriptions frame on each stream, so that a reload that switches email
off reaches the gateway. Platform agents always have the hosted GitHub
source and sent a frame already.
inbox, as GitHub records do.
Tests
New
tests/test_hosted_email_channel.py: the contract samples, thesetting, the provider entry, the subscription entry, the email event over a
real stream with the email connection's capabilities (a turn in the
conversation's session, nothing posted, the note line, the session kept past
the sticky period, one session per thread), a switched-off email, the frame
with and without email, a reload that switches email on and off, and
nerve doctor. Startup tests for the email channel and the warning.🤖 Generated with Claude Code