Repository navigation
Releases: crissmoldovan/agent-communications
Release list
0.36.0
What. Workflows that branch, wait, finish the job and tell others, in the command line, the agents' tools and the
desktop app:
- One list of workflows. What were rules are workflows; a straight line is one shape a workflow can have, not a
kind of its own.agent-events workflow …, and Events → Workflows in the app. - A workflow can be a graph of steps. A condition or a judge; Which project?; find a session (in a project, a
worktree, or the one that took this conversation); start one; make a worktree; hand the message over; wait for a
turn's end, a free session or a time; a Linear issue, then a comment, a change or closing it; tell me; split; and
raise an event. You draw it in the app's designer, write it as text, or describe it in your own words and have a
model draft it (Describe it). Each run is shown step by step, and you can cancel it. - Projects. A GitHub repository, its folders on this Mac, a profile and hints, found by a scan. Which project?
chooses among them. - Events from workflows. You tick, step by step, the events a workflow raises, or add a Raise an event step.
Each is a CloudEvent named<namespace>.<workflow>.<step>.<event>. A subscriber you approve receives them live on
this Mac (SSE or WebSocket) or by signed webhook, and a raised event can start another workflow about the same
message.
Why. The owner, 2026-10-27: "a workflow designer based on a node-style thing: 1. An event comes in. 2. Conditions
are set. 3. If they are met, it continues. 4. It can search for the session and determine which session it is. 5. If
it's about that session, it goes there. 6. If the session is found, it goes there. 7. If not, search for another one. 8.
If no session was found, it goes to create a new session. 9. If it has to create a new session, you should check what
project it is in. 10. It should be creating a separate work tree, optionally or not. 11. On completion, I may also want
to say add part of this behaviour to this workflow, like create a Linear ticket. 12. … create instructions on what the
Linear ticket should contain. 13. When it finishes work, it should actually close the Linear ticket, and so on."
Then, on the prototype: "I may want to raise an event … Third-party things can listen to them"; "I don't think I
understood the difference between workflows and rules"; and "Emit events for every single node … It should be able to
raise custom events as well." The prototype is docs/prototypes/2026-10-27-workflows/, and the design is
docs/superpowers/specs/2026-10-27-workflows-design.md.
Impact. A minor release. Your workflows keep working as they are:
- Nothing you have changes its bytes. Every stored workflow version keeps its document and digest, and a version
with noflowruns exactly as before. On its first start the event service's database moves to schema 34. That adds
the tables for runs, projects, events and subscriptions, and rebuilds the deliveries table, copying every row, to
admit the new kinds. An olderagent-eventsthen refuses to open the database, as after every schema change. - The word is "workflow".
agent-events workflow …andworkflows listare the names, andagent-events rule …
still works. The MCP tools keep their names (events_rule_*). The text's first line isworkflow <id>, version <n>,
andrule …still parses. Every message, preview and refusal says "workflow", so a script that matched the word
"rule" in the output has to change. - Live streams now listen. Before 0.36.0 nothing listened for a live-stream target: its frames were kept and never
served. Now the event service starts a loopback listener for each subscriber version an active workflow's live-stream
target binds, or one with an approved subscription, and closes it when that ends. A port another program holds shows
asPORT_IN_USEinsubscribers list. Frames reach an open stream as they are delivered, and replay from
Last-Event-ID. - Webhook signing secrets made before 0.36.0 never signed. They were made in base64url, which the signer refuses.
So every delivery to a webhook target whose secret was generated failed withBAD_DATA. New secrets are standard
base64. Rotate each webhook target's secret (agent-events target secret rotate <target-id> --version <n>) and give
the receiver the new one. The old secret is left out of the overlap, so the new one signs at once. - Approving a workflow while the event service ran could undo it (since 0.16.0). When the service's tick picked up
the activation an approval was completing, the approval could report it cancelled when it was on. Or the workflow
stayed on but refused every delivery until it was approved again. Now each tick resumes an activation only under its
approval's own serialisation, from a fresh read. - A flow does nothing until you approve its version. Its limits start low: 10 runs an hour, 2 at a time, 2 sessions
started an hour, 5 worktrees a day. A session starts in the mode its step names (manual,acceptEditsorplan),
neverbypassPermissions. - Projects, runs and Describe it are yours alone. No tool reads or runs them. An agent can check a workflow, read
the step event catalogue, and propose a subscriber and prepare its approval, never approve it. - To use it: update with
agentcomms updateand the app's own update. If you have webhook targets, rotate their
secrets as above.
In the desktop app
- Events → Workflows: one list of every workflow with its version, approval, runs this week and last run; New
workflow… from your words, an empty canvas or a copy. - The designer: the canvas (drag a step from the palette, wire its outcomes), the text and the JSON, three views of
one document, with the checks under them. Each step's settings have its events to tick, and the workflow's settings
its limits, event names and chain. Describe it… drafts the workflow on your Claude subscription, or with the
OpenRouter or Anthropic key you keep for Analyse. It marks each step with the words it came from and asks up to five
questions. - Approve and try: the approval lists the workflow's numbered effects. Try walks the flow with the outcomes you
choose, or runs it on a recent message, as a "would". - The run view: the version's graph lit along the path the run took, each step's time, outcome and wait ("Needs you
in claude-stream"), the events it raised and who received them, and Cancel. - Projects in the sidebar: the repositories found on this Mac, their folders, the sessions working in each (busy
or idle), the profile and hints. - Events → Subscribers: who receives which events, live or by webhook, its listener or address and backlog;
Add a subscriber…, Approve…, Remove. Tokens and secrets are made at the terminal. - Messages: a message's story follows its workflow: the project, the issue, the session, the turn's end, the events
and the close. The lists say each run's story in a few words.
In detail
@agentcomms/events: theflowdocument (checkFlow,flowOf, which draws a straight-line workflow as its
graph) anddescribeFlow, the numbered effects an approval lists. The text grammar gains steps, arrows, events,
Raise and chains, round-tripped. Appendix W is the step event catalogue v1, withmatchesEventPatternand the
CloudEvent's fixed attributes.@agentcomms/events-daemon:- The engine and its steps: one run per match; steps claimed and settled once, adopted after a crash, never two;
splits, skips, limits (64 executed steps), expiry and cancel. - What a step reaches: git runs as you, without the repository's hooks, inside the project's worktree root.
Linear changes reach only the issue the run made. - Commands:
runs list|show|cancel,projects list,project show|add|update|remove|scan,workflow check|describe|test-retained,workflow-events list|show,subscriber enable|approve|secret|url, andworkflow show --version. - Delivering events: a sealed event outbox; one dispatcher, at least once and in order per subscriber, apart from
the run, bounded per workflow, overall and per subscriber. Live listeners serve SSE and push-only WebSocket (no
library, the same token, origin and replay rules), and webhooks go out signed (Standard Webhooks). Chains stop at
depth 3. A shadow version's events are dry-run records.
- The engine and its steps: one run per match; steps claimed and settled once, adopted after a crash, never two;
@agentcomms/core:TakeStore.threadHolder, the session that took an earlier message in a conversation.
Approvals gain thesubscriptionactivation kind.- Security review fixes before release (an independent review of this release, 2026-10-27):
- "The session that took this conversation" could be named by an agent: a command run from an agent's shell states
its own Claude Code session id, so it could take a message as any session and send the conversation's next
messages there. Now only a take by an agent window's own session counts, and the session found must work in the
folder that window worked in, inside the relay's allowed roots and outside Agent Comms' own folders. - A live listener the event service no longer needs closes within a moment, whatever its clients do. Before, a
request left half-sent on its port — which an agent could open from its shell before removing the subscriber —
stalled the whole event service until that client went away. A listener also cuts a request whose headers take
more than 10 seconds, and takes at most 64 connections. - A live stream whose reader leaves more than 1 MiB unread is closed rather than buffered for; it replays what it
missed when it reconnects. A stream opened with the previous token closes when that token's five-minute overlap
ends, not at its next frame, and a listener writes only its own subscriber's frames. - More than 64 subscribe...
- "The session that took this conversation" could be named by an agent: a command run from an agent's shell states
Agent Comms 0.36.0
Agent Comms 0.36.0.
macOS: the DMG is signed with a Developer ID and notarised.
Windows and Linux: the .exe, .AppImage and .deb are unsigned; your system warns before it opens them.
What changed: CHANGELOG.md, 0.36.0.
0.35.0
What. Three things, each in the command line, the agents' tools and the desktop app:
- Sessions take a message before they act on it. When you turn it on for an account, one session takes a message
and no other can act on it; it can hand it back ("not mine", with a reason) or mark it done, and you see who has
each message. A rule's delivery offers the message to the window or session it names, which must still take it, and
a message nobody takes within the time you set can remind you. - Search on this Mac. What your agents read in Gmail and Slack, and the replies Agent Comms sent, kept on this Mac
and searchable by words and, with a local model through Ollama, by meaning; a page per person. Off until you turn it
on. - One MCP connection per app. An app (Claude Code, Codex, Cursor, VS Code, Claude Desktop, or Manual) gets one
entry,agentcomms, that brings every part you choose — Agent Comms itself, Gmail, Slack, Resend, WhatsApp, Events —
through one gateway, with its own key. Several connections per app (one per project or profile), each changed later.
Why. The owner, 2026-10-26: "David messages me, the session checks and acknowledges it… The acknowledgement should
be mandatory so no other sessions are getting it… the session got the message and it determined this is not for me, so
it was just handed back"; "where can I find the place where I can see things like David messages me", with a local
vector database; and on the MCP screen, "I honestly thought I would have one single MCP server… what I want is
instances… add a new connection… choose the type… give it a name… generate a token… I could have multiple instances".
The screens were drawn first and approved (docs/prototypes/2026-10-26-messages-lifecycle/, and the connection boards in
docs/prototypes/2026-10-26-0340-rework/). The designs are docs/superpowers/specs/2026-10-26-taking-messages-design.md,
2026-10-26-local-search-design.md and 2026-10-26-single-server-design.md.
Impact. A minor release. Nothing changes until you turn it on, except one default:
- A channel's own
mcp installnow joins the app's connection (agent-gmail mcp install --client claude-code
adds Gmail to Claude Code's connection, or makes one with Agent Comms itself and Gmail).--standalonekeeps the
old separate entry;--force,--print,--nameand--client jsonnow need it. - Your existing MCP entries keep working.
agentcomms connection migratemoves an app's separate entries into one
connection, when you ask, and--undoputs them back. - Taking is off for every account until you turn it on (Settings → Taking messages, or
agentcomms taking). Turning
"must take" off again, a 👀 on taking, quieting the reminder and making it wait longer are approved like any
loosening. - Search is off until you turn it on; what it keeps follows Activity's days, and Delete everything removes it and
its key. Its text is encrypted at rest; its words index holds keyed hashes of words, never the words; the vectors are
not encrypted, and the screens say so. - Approvals say which connection asked (
preparedBy); 0.34 reads these records unchanged. - To use it: update with
agentcomms updateand the app's own update, then restart your MCP clients. To move an
app to one connection: the app's MCP connections → Move to one entry…, oragentcomms connection migrate.
In the desktop app
- Messages: who has each message — Taken by an agent, Handed back, Nobody took it — with the story's Who has it:
Take back, Give to… and I'll handle it, applied at once; filters for Nobody took it and Handed back. - Queue: a Not taken tab, counted in its badge.
- Settings → Taking messages, mirrored in each account's Agents here.
- People, Ask about past messages (Messages' second tab) and Settings → Search on this Mac.
- MCP connections: each connection with its status and last use; Add a connection step by step, ending with
what was written and what to restart (for Manual, the snippet with its key, shown once); a connection's page with
Rename, Rotate key, Change parts (narrowing at once, widening approved) and Remove; Move to one entry… with Undo;
and "Used by" in Activity.
In detail
- Taking (
@agentcomms/core, the channels,@agentcomms/events-daemon):agentcomms taking <account> …and
comms_taking;gmail_take/gmail_taken,slack_take/slack_taken,resend_take/resend_taken;agentcomms takes list|release|give|keepfor you. A rule's delivery offers the message to the lane's window (until it is woken,
at most the account's reminder time) or to the session it posts to (15 minutes); the session still takes it itself.
A session's prompt says until when.events_lane_itemsays who has the message, in closed words. Over MCP, other
sessions are never named. An agent's shell that joined no window is never you: where a take is required it is
refused withNO_SESSION. Turning "must take" off, a 👀 on taking, notify off and a longer wait are approved; the
rest applies at once. Adonelapses after 30 days. - Search (
@agentcomms/core):agentcomms search status|query|people|person|show|index|rebuild|link|unlink|label| on|off|accounts|model|delete. No agent searches, over MCP or from its shell, and Delete everything is yours alone.
The words index holds keyed hashes of words, not the words, and keeps no word order; a pruned message's words leave
the file. Gmail's send-as addresses count as you. More of Activity's days, while Search keeps words, says so in its
preview. Meaning comes only from Ollama at a loopback address, with a model from the reviewed list. - Connections (
@agentcomms/core):agentcomms connection add|list|show|change|rename|key replace|remove|migrate,
with matchingcomms_connection_*tools andagentcomms mcp --connection <id>, the gateway. A connection's key is
stored only as its SHA-256 and checked in constant time; the parts start without it. It is not a password: any
process running as you can read it from the app's config, and the gateway's own environment shows it while it runs.
connection listsays which connections a migration made, andmigrate --undorefuses the rest and pins by id.
activity sessionsnames the connection a session used;channels --jsonsays what each part takes. Approvals say
which connection asked (preparedBy), as the gateway reports it. - Security review fixes before release (an independent review of this release, 2026-10-27):
- Removing a connection whose entry shadows a wider one (one project's, over the app's every-folder connection) is
approved like any loosening; its preview says what agents there would use instead. - An agent can rename, re-key or remove only its own connection; every preview names a connection by app, place and
id; the gateway's instructions no longer carry a connection's name. - The gateway stops serving when
config.jsonbecomes unreadable; a re-key never writes back an entry you removed. - An agent can change the parts of only its own connection, narrowing included; a remove checked while the app's
config can't be read waits for you; a new connection name can't hold brackets or "…". - A refusal caused by an agent's marker in your own shell names the variable and how to clear it (
CODEX_HOMEis
one people export). - When Activity's settings can't be read, Search keeps words 30 days, not a year.
- The take and search fixes above (
NO_SESSION, lapsing offers, hashed words, the retention line, Delete
everything).
- Removing a connection whose entry shadows a wider one (one project's, over the app's every-folder connection) is
- Gmail's setup joins the connection like
mcp install, and leaves a Gmail part kept to another mailbox alone,
printing theconnection changecommand that would reach the new one.
0.34.0
What. The desktop app explains itself, and wears cue-ui's soft look:
- Your agents (was Agent): what people see when an agent writes for you, a Slack message and an email From line
with your tag, then your name, the word after it and the exceptions, each changed in the app. - Each account has a page, with Agents here: what agents may do there, how their messages look in a Slack
workspace, and the replies you approved in advance. - Events → Service in plain words, with Switch on… approved in the app and Switch off; Deliveries says
when a rule is in dry-run, and what can reach an agent. - Connections in plain words: "starts an older Agent Comms", by the same yardstick as
update, with Bring
everything up to date; several servers added to an app in one approval. - The soft look from cue-ui 0.34.0, with gliding marks, a two-finger swipe between tabs, and a Typeface choice:
Inter or SF Pro. - Doctor's checks keep their names readable beside a long result.
The command line and the MCP servers gain what these need: --app-approval on label, behaviour and update,
mcp install --server <a,b,c>, agent-events collection approve, and disable-all saying what it did.
Why. The owner tried the 0.34 test build and could not read it: "I don't understand what it says behind
'core'", "It says it's running, but below it says 'collecting off.' Why?", "the agent itself is not really
understandable". The screens were redrawn first, as a prototype the owner approved ("the UI looks good"), kept in
docs/prototypes/2026-10-26-0340-rework/. cue-ui shipped the soft look the 0.28.0 redesign had drawn locally (CUE-571).
The design is docs/superpowers/specs/2026-10-26-desktop-0340-design.md.
Impact. A minor release. Everything is added; nothing is renamed or removed, and nothing needs migrating.
- More is approved in the app, on macOS: your agents' word, name and exceptions, a Slack workspace's style, what
agents may do on an account, switching the event service on, and updating Agent Comms. On Windows and Linux each
still goes to the terminal, and anything that only narrows still applies at once. - The test build changes only its own test profile: it refuses to approve anything that would touch your real
folders or apps, and its event service accepts the test build only on the test profile's own state. - Agents see less: the approval view an agent gets, through its tools or its own terminal, carries no challenge;
a refusal is audited as the app's only when the app it really was. - Your look resets once: a look picked in 0.28 to 0.33 is not carried over; the app opens once on soft at the
normal rung. Pick cue or terminal, a density, a mode or a typeface again, and it stays. - To use it: update with
agentcomms updateand the app's own update, then restart your MCP clients.
In the desktop app
- Your agents replaces Agent: the previews, Your name and The word after it (each Change… approved in the app),
Exceptions with Add… and Remove, and one line per account opening its page. - Accounts: each account opens its page, with About and Agents here:
- What agents may do here: a switch per act; off applies at once, on asks you to approve it in the app. Slack
adds overrides for one conversation, Gmail for some people. - How their messages look here (Slack): as the Agent Comms app, or as you, tagged, each with a preview.
- Replies approved in advance (Slack): each one's contents, places, limits and end, with End and End all.
- What agents may do here: a switch per act; off applies at once, on asks you to approve it in the app. Slack
- Events → Service: Switched off, On or Paused, each in a sentence that says what it means, with Switch on…,
Pause, Resume and Switch off where each does something. Deliveries: a dry-run line for a rule in dry-run, a
Dry-run log filter, removed records folded, and Something reached your agent?. - Connections: older entries said in plain words, Bring everything up to date, Change… adding several servers in
one approval, and the test build's banner. - The look: cue-ui's soft preset (the
softstyle set and theglazefinish) in place of the app's own copy; the
chosen tab, segment and sidebar item glide; two fingers swipe between tabs; reduced motion is respected; the palette
menu's Typeface offers Inter (bundled) and SF Pro (macOS's own face). - Doctor: a check's name keeps a third of its row beside a long result, and a fix has its own line.
In detail
- App approval in core (
@agentcomms/core):--app-approval <id>now works onlabel word|name|rule|icon|style,
behaviour allow|clearandupdate, as onmcp installsince 0.32.0. It counts only from the attested app on
macOS; elsewhere, or from an agent, the command refuses withAPPROVAL_REQUIRED(exit 10) and names the words to
type. An approval handed another change than the one it previewed is void (APPROVAL_VOID). The test build's
approval counts only when every folder the command touches is inside its marked test profile, judged from the
account's own home, not from the environment. - Several servers in one install (
@agentcomms/core):mcp install --client <c> --server core,gmail,eventsand
comms_server_installwithservers: one preview, one approval bound to that list and order, registered in order
and stopped at the first that fails, naming what was and was not registered. - Switch on, approved in the app (
@agentcomms/events-daemon):agent-events collection approve <approval-id> --digest <digest>approves the pendingenable-allfrom the attested app; the terminal'sapprove <id>still
works.disable-allreturnssaid, a sentence of what it did. The event service accepts a test-build approval only
on the test profile's own state folder. - No challenge for an agent (
@agentcomms/events-daemon):events_approval_view, andapproval viewin an
agent's shell, return the preview without a challenge, so nothing an agent holds can approve, and an agent's look no
longer replaces the challenge you are typing. - Audit: a refusal is recorded as the app's only for an attested app; a mismatched digest is
refused. - cue-ui 0.34.0:
@cueplusplus/uiand@cueplusplus/tokens0.34.0,@cueplusplus/theme-soft1.0.0, with
@fontsource-variable/interand@fontsource/ibm-plex-mono5.3.0 (OFL-1.1) bundled. The approved mockup is kept in
docs/prototypes/2026-10-24-soft-look/.
Agent Comms 0.35.0
Agent Comms 0.35.0.
macOS: the DMG is signed with a Developer ID and notarised.
Windows and Linux: the .exe, .AppImage and .deb are unsigned; your system warns before it opens them.
What changed: CHANGELOG.md, 0.35.0.
Agent Comms 0.34.0
Agent Comms 0.34.0.
macOS: the DMG is signed with a Developer ID and notarised.
Windows and Linux: the .exe, .AppImage and .deb are unsigned; your system warns before it opens them.
What changed: CHANGELOG.md, 0.34.0.
0.33.0
What. Rules you can edit, analyse, and have act:
- Edit a rule in the app, visually or as text. Each mistake is shown as you type, with its line.
- Analyse suggests how to sharpen each part of a rule, and changes nothing until you say so.
- Two new things a rule can do: add an issue in Linear, or hand the message to a coding-agent session that your
claude-stream relay runs. If that session is busy, the message waits its turn. - What a target is told can be fixed text, fields mapped from the message, a model's words, or a file.
The command line and the MCP servers gain the commands behind these: rule analyse, judge show, integration show linear, session-queue list|drop, and the linear and anthropic provider keys.
Why. While reviewing the 0.32 prototype, the owner asked for four things:
- to edit a rule "visually or as text";
- an Analyse that gives "a more enriched version of the rules";
- targets that "add to Linear" and "post message to session … if it's not present, create a new session … if the
session is busy, enqueue it"; - context that is "generated by an LLM, fixed by us, or just something derived from the inputs … Currently, we cannot
handle that."
The screens were prototyped first and approved, in docs/prototypes/2026-10-26-0320/. The design is
docs/superpowers/specs/2026-10-26-rules-that-act-0330-design.md. The Agents screen and the digital twin were
prototyped and specified there too, and are not built: the Agents screen waits on claude-stream.
Impact. A minor release. Everything is added; nothing is renamed or removed.
- Migrations: five event service migrations run on their own:
- 29: a key slot for Anthropic;
- 30: a key slot for Linear;
- 31: a delivery's Linear issue;
- 32: the session target and its busy queue;
- 33: a judged decision's sending domain.
- Behaviour changes to know about:
- On Windows and Linux, these changes are made at a terminal:
judge setup,provider key set|remove,
integration set agent-sessionsandmcp install --app-approval. Nothing there attests that the app started
them, so from anywhere else they refuse with exit 10,ORIGIN_UNATTESTED, and change nothing. The app offers Do it
in Terminal. Nothing changes on macOS. - A judged decision keeps the sender's domain, never the address, encrypted. It is kept only while the rule keeps
the message's content. - New addresses the event service may reach, each only when you use it:
api.linear.app, for a rule that adds Linear issues;- OpenRouter or Anthropic, when you press Analyse;
- your own claude-stream relay on this machine, for a session target.
- On Windows and Linux, these changes are made at a terminal:
- Agent sessions need claude-stream. It is not part of Agent Comms. A session target does nothing until you name
its relay in Settings → Integrations, or withintegration set agent-sessions. - To use it: update with
agentcomms updateand the app's own update, then restart your MCP clients.
In the desktop app
- Events → Rules:
-
New rule… and Edit… open the rule editor. You can edit as Visual, Text or Advanced: JSON. Visual has five
parts:- When;
- Which messages;
- Ask a judge;
- Then do;
- What's kept, and the limit.
-
Text is checked as you type. A part the visual editor has no control for stays as text, and is never dropped.
-
The judge picker offers every live judge.
-
Then do can:
- put a message in the Queue;
- send it to an agent session;
- add an issue in Linear;
- send it to a webhook;
- stream it to subscribers;
- write it in the dry-run log.
-
Analyse suggests changes to each part, using your OpenRouter or Anthropic key. You see every change before it
is made. -
Review and approve says everything a target is given:
- a session's every context part;
- a Linear issue's team, labels, assignee, title and description;
- where each mapped field comes from;
- that claude-stream journals each prompt.
On Windows and Linux it offers Approve in Terminal.
-
- Settings → AI providers: a row for an Anthropic key, for Analyse.
- Settings → Integrations (new):
- Connect Linear… with a personal API key;
- Agent sessions: where your claude-stream relay is.
- Events → Deliveries:
- Waiting for a session shows each match waiting for a busy session, and since when. Drop… asks first.
- A Linear delivery names the issue it added, with Open in Linear.
- On Windows and Linux:
- Do it in Terminal for judge setup, provider keys, Linear and the relay;
- Approve in Terminal in Connections.
In detail
- A rule as text (
@agentcomms/events):printRuleTextwrites a rule as the short language the app's text editor
shows (when a new message arrives in cue/gmail, in the inbox,match any,ask,then,keep,at most), and
parseRuleTextreads it back to the same document, with each mistake's line and column. Anything the text has no
words for is kept asrawJSON, so nothing is dropped. The grammar is inpackages/events/src/rule-text/GRAMMAR.md. - Analyse (
@agentcomms/events-daemon):agent-events rule analyse <ruleId> [--draft -] --jsonsuggests how to
improve a rule part by part — from its own text and content-free counts of its last 30 days (a sending domain the
judge always refused, a bar between kept and dismissed scores, a rule that never matched), and from a model on your
own OpenRouter or Anthropic key for the judge's question, a session's text and a Linear title — and never applies
one. It reads and sends no message content, and runs only at your terminal or in the app.provider key settakes
anthropic; a judged decision now keeps its sender's domain, never the address, encrypted and apart from its
metadata, only as long as the rule keeps the message's content. Two automatic migrations: 29 gives Anthropic a key
slot, and 33 keeps the sending domains. - A rule can add a Linear issue (
@agentcomms/events-daemon,@agentcomms/events):- The key: your Linear personal API key is kept beside the AI provider keys:
agent-events provider key set linear, from the app or your own terminal, never from an agent.provider listnames Linear too. - What it can see:
agent-events integration show linear [--team CUE] --jsongives the workspace and teams the
key can see, and a team's labels — the person's read for Settings → Integrations, refused inside an MCP server. - The target:
{ kind: "linear", team, title, description, labels, assignee }. The title and description are
text with{{field}}slots, each naming one of the rule's mapped fields, checked when the rule is activated; a
model-written one comes with the session target. The approval preview names the team, the labels, the assignee
and which fields fill the title and description. The rule text says it:linear issue billing-issues, version 1, in team CUE, label Billing, assigned to me. - A slot's value stays text: in the description every slot's value — what the sender wrote, or a constant the
rule's mapping sets — is a block quote labelled with who wrote it, inside a code block, where Linear makes no link
or mention from a URL,@nameor issue id; in the title it is flattened to one line. - Checked against Linear: when you enable the rule with a Linear key kept, each team key must be the team its id
names in Linear, and each label name its id's (LINEAR_TEAM_MISMATCH,LINEAR_LABEL_MISMATCH), so the approval
cannot name one team and send to another. With no key the check is skipped, and the approval says so. - Delivery: one
issueCreatethrough the pinned connection toapi.linear.app/graphql, the one Linear address
the event service may reach. A rate limit or a 5xx is tried again as a webhook's failure is; Linear refusing it
fails at once with Linear's code. A retry never adds a second issue: each try looks for the issue by its own id
first.deliveries listanddelivery showname the issue (linear: { identifier, url }), never what it says. - Migrations: two event service migrations run on their own: 30, a key slot for Linear, and 31, a delivery that
can record its Linear issue.
- The key: your Linear personal API key is kept beside the AI provider keys:
- Agent sessions, through claude-stream (
@agentcomms/events,@agentcomms/events-daemon): a rule'ssession
target hands each match to a coding-agent session your claude-stream relay runs — the one working in a folder,
started there with Claude Code or Codex if there is none, or the one behind the window you bound to a lane — in the
mode the rule fixes (manualby default;bypassPermissionsis refused). Its context is fixed text, the mapped
fields, a model's words (on your OpenRouter or Anthropic key) and a file (at most 64 KB, under an allowed folder), in
a fixed frame with sender material in the untrusted-content envelope. A busy session queues the match until its run
ends;session-queue list|dropshow and drop what waits, and a bound window sees its own lane's with
events_session_queue_list.integration show|set agent-sessionssays where the relay is — yours to set, from the
app or your terminal. The rule text prints and parses thesessionblock. One automatic migration, 32, admits the
session kind and adds the content-free busy queue.- From the security review: the control token goes only to a port your own process listens on (checked with
lsofon macOS and/proc/net/tcpon Linux; not on Windows), and is read from the opened file itself. Only a live
run's 409 is busy. A busy session never fails a match: it waits until the session is free or the match expires,
tried further apart the longer it has waited (at most every five minutes), and only other failures count towards
the 12 that fail it. A session since watched, fenced or mo...
- From the security review: the control token goes only to a port your own process listens on (checked with
0.32.0
What. The app answers the owner's feedback of 2026-10-26, item by item:
- Judges and keys in the app: Jev and a local model server are set up in Events → Judges, and Settings → AI providers
keeps the TypeSafe and OpenRouter keys. - Readable rules and deliveries: Rules and Deliveries read in plain words, and each one opens to show its details.
- The 1970 date is gone.
- Messages: a new screen shows what happened to each message that came in.
- Connections: a new screen shows which apps have which servers.
- The test build tells you it is one.
The command line and the MCP servers gain the reads and fields these screens need.
Why. The owner installed 0.30.0, opened the app and found it hard to use:
- turning a judge on meant the Terminal;
- a key had no home;
- rules and deliveries were raw database rows ("mail subject version 1 state active and approval and activation", "the
next try is always 1970"); - nothing answered the question they care about most: "something has come in. Did we acknowledge it? Did we reply to
it? Is it something that has been passed over to a session, or was it proactively read by a session?"
The screens were drawn first, as a prototype the owner approved ("perfect"), kept in the repository at
docs/prototypes/2026-10-26-0320/.
Impact. A minor release. Everything is added; nothing is renamed or removed.
- Migrations: three database migrations in the event service run on their own: provider keys, a queue item's
history, and clearingnext_at. - Behaviour changes to know about:
- The dry-run log (
dryrun listanddryrun show) is now readable with--jsonoutside an MCP server, so the
app can show it, like the queue's content since 0.30.0. delivery dropalso stops a delivery that is waiting or will be tried again.- Every MCP server marks its own process, so commands only the person may run refuse inside any of them.
- Approvals from the app: a change can now be approved in the desktop app. That covers turning a judge on, and
adding a server to an app from Connections. The audit records it as approved in the app. Everything else stays at
the terminal, as before.
- The dry-run log (
- To use it: update with
agentcomms updateand the app's own update, then restart your MCP clients.
In the desktop app
- Events → Judges:
- Set up… for Jev, choosing OpenRouter or TypeSafe directly (a provider without a key is greyed out), and for a
local model server (Ollama's System One, on this Mac). - Turn off applies at once.
- Laya reads "Not available yet".
- Set up… for Jev, choosing OpenRouter or TypeSafe directly (a provider without a key is greyed out), and for a
- Settings → AI providers: add, replace or remove the TypeSafe and OpenRouter keys. A key is never shown again
once saved, and removing one a judge uses names that judge first. - Events → Rules:
- one card per rule, with the rule as a sentence, its state and its week;
- opening it shows what it watches, which messages, the judge, where they go, what's kept, the limit, its versions
and, folded away, the technical ids explained.
- Events → Deliveries:
- "rule → where it went", with the state in plain words, and no 1970;
- a delivery's page shows the message, why it was passed on, a dry run's record (the sender's words marked as
outside text) and its tries, with Try now and Stop trying.
- Messages (new): each message that came in, how far it got, and the steps on the way. Opening one shows its story,
built from what Agent Comms saw. - Connections (new):
- Claude Code, Claude Desktop, Codex and Cursor against the six servers, and when each was last used;
- who can reach the servers: local windows yes, cloud sessions no;
- Change… adds a server once you approve it in the app, and removes one at once.
- The test build: an orange "TEST BUILD" band naming its branch and commit, the name "Agent Comms Test" and the
test profile's folder, and its tray says so too. A released app looks as before.
Activity ties a reply to the message it answers (Task E of the owner's 2026-10-26 feedback, for the app's Messages
screen).
- New ids on existing activity lines. Every field is new, and none was removed or renamed.
- Gmail: a reply draft (
gmail.draft.reply) names the message it answers and its thread (replyTo), and the draft
it made (draftId). A forward answers nothing, so it has noreplyTo. - Gmail: preparing a send (
gmail.send.prepare) names itsdraftId. When the draft answers a message in this
mailbox, it also names that message (replyTo). The prepare already proves this, so Gmail is asked nothing more. - Gmail: a send (
gmail.send.execute) names the draft it sent (draftId) and the thread Gmail filed it in
(threadId). - Slack: a post (
slack.post.send) names its conversation by id (channelId) and the thread it went into
(thread: the parent's ts, or null for a new message). - Slack: a history read (
slack.read) names its conversation by id (channelId), beside the name it already kept. - Slack: an acknowledgement under a standing approval (
slack.ack) says what it did (act:replyorreaction),
so a message an agent answered reads apart from one it only marked seen.
- Gmail: a reply draft (
agentcomms activity items [--since <ms>] [--ids <id,…>] --jsonlists every call that names an item, grouped
by the item. A Gmail message keeps its id, and a Slack message is<channelId>/<ts>. Each line names the window
(key, folder, client), the operation, when it ran and how it ended, withreplyTo,thread,draftIdandact
where it has them. A line appears under each item it names and each item it links to, so a message leads to its
reply draft, and that draft to its send. Subjects and senders are the sender's words, returned in the
untrusted-content envelope. It is the person's view: refused inside an MCP server's process (exit 10), with no MCP
tool, likeagentcomms activity show.- Every MCP server marks its own process as it starts. Core's server and the Gmail, Slack, Resend and WhatsApp
servers now setAGENTCOMMS_MCP_SERVER, as the event service's already did, and@agentcomms/gmail-mcpdoes too.
Every program a server starts inherits the mark, soagentcomms activity itemsand the event service's person-only
reads refuse there as well. Your terminal is not marked, and nor is a shell an agent's client runs itself. agentcomms mcp uninstall --client <c> --server <s>, andcomms_server_uninstallfrom a chat, take one server
out of one client. They remove every user-scope entry that starts it, whatever it is called. This is a tightening:
it applies at once and asks nobody. Each removed entry is saved first, owner-only, and the change is audited.- A project's own entry is left in place, and the result says where it is.
- A config file with comments is left as it was, and so is an entry whose client command can't be found. The result
names each. - The client keeps the server running until it restarts.
agentcomms mcp pruneis unchanged: it removes
runtimes, never a registration.
What happened to a message, in the event service. The owner asked: "something has come in. Did we acknowledge it?
Did we reply to it? Is it something that has been passed over to a session, or was it proactively read by a session?"
The event service now keeps and gives the part it sees; the app's Messages screen comes after the owner's feedback on
its prototype.
agent-events messages list --jsongives each message the event service observed, newest first: what each rule
decided and what its judge answered, where each delivery went, the queue item it became and what happened to that
item. It looks back 30 days and gives 100 at a time by default (--since,--before,--limit), with anextto
pass back for the page after.agent-events message show <event-id> --jsongives one of them, with every line of its queue item's history.- Each message carries its source's own ids: a Gmail message id, a Slack channel and ts, a WhatsApp chat and
message id, a Resend email id. These are what the app joins a message to its Gmail or Slack link and to the activity
log with. When the queue kept a summary, it also carries the sender and subject, marked as from outside. - Queue items keep a history: woken (a lane's bound window was woken for it), read (that window read it), done,
dismissed and reopened, each saying whether an agent window did it, and which, or you did, at the terminal or in the
app. It goes when the item goes. - Only you see it. Both commands refuse inside the event service's MCP server, and no MCP tool returns a message's
story or an item's history. The history never enters the activity log, so no agent's activity view can name an item
in a lane bound to another window. - One automatic database change adds the history table and two indexes. Nothing is asked of you.
Rules and deliveries you can read. The owner opened the app's Events screens and found raw database rows: "mail
subject version 1 state active and approval and activation", and a next try "always 1970".
- No more 1970. A delivered dry-run, queue or SSE delivery kept the "due now" time it started with, which the app
showed as a next try on 1 January 1970. Delivering now clears it, and a migration clears it on the rows already
delivered, cancelled or expired. - A rule in plain words.
agent-events rule show <rule-id>(andevents_rule_show) gives each version with when
it was switched on, replaced or revoked, a one-sentencesummarysuch as "When mail arrives in cue/gmail with
'invoice' in the subject, ask Jev '…'. If it says yes, put it in the Queue, lane billing.", and what it did in the
last 7 days: matched, judged yes and no, held, delivered and failed (over MCP, not for a lane the window isn't
bound to). Every word the rule's author wrote is marked as theirs. The approval previ...
0.31.0
Routing: a lane wakes the agent window you bind to it. The third step the owner asked for, after activity and the
queue: "when David is messaging me on Slack", route it to the right agent session.
- A lane is a queue with a name. A rule can send its matches to lane
david, as it sends them to a queue. You
approve that the lane's items may be read by the window you bind, with the rule, as every target is approved. - You bind a window to a lane, at a terminal:
agent-events lane bind david --window <id>. The app's Queue screen
has a Lanes tab, with Bind a window…, which lists your recent agent windows and opens Terminal with the command,
and Unbind, which applies at once. An agent can ask for its own window's id (events_lane_whoami) to tell you
what to bind, but it can't bind itself through MCP. - The bound window wakes on its own. In Claude Code,
events_watchwaits as an MCP task. When a new item arrives,
the idle window starts working without you typing, told only "1 new item in lane david (Slack, rule …): read it with
events_lane_item". It never gets the sender's words in that notice. It reads the item throughevents_lane_item,
marked as from outside, and then watches again.events_waitwaits up to 50 seconds for clients without tasks. - Who sees what: only the bound window may read a lane's items, and only those that arrive after it was bound.
Every other agent window sees no lane items at all, not even in the queue tools. You see everything in the app and
at the terminal. - The event service's MCP server now speaks the newer MCP protocol (2026-07-28) that tasks need, and still serves
clients on the older one. - What doesn't change: a queue without a lane works as in 0.30.0. Windows that can't be identified (on Windows, or
a server started in your home folder without a Claude Code session id) can't bind yet, and are told why.
Agent Comms 0.33.0
Agent Comms 0.33.0.
macOS: the DMG is signed with a Developer ID and notarised.
Windows and Linux: the .exe, .AppImage and .deb are unsigned; your system warns before it opens them.
What changed: CHANGELOG.md, 0.33.0.