Instant messaging
Instant messaging pulls every chat app you use into one page: Slack and Telegram over their real APIs, WhatsApp, Messenger, and LinkedIn through the browser extension, since none of them offer a usable API for this. Every inbound message becomes an event, flows through triage like mail and calendar do, and can land a row in your task queue with a reply box right there.
The connector interface
Section titled “The connector interface”Every messaging app implements the same small seam: {fetch, send, capabilities}.
fetch(since?)drains everything new since a watermark and returns it as a normalized{conversationId, conversationTitle?, providerMessageId, ts, sender, body, meta?}list.send(conversationId, body, opts?)posts a reply;opts.claimKeylets a connector de-duplicate a retried or double-fired send so it never queues the same message twice.capabilitiesreports whether the connector can send at all and the latency you should expect, shown in the UI so you know what you’re waiting on.
Native (API) connectors
Section titled “Native (API) connectors”Slack and Telegram connect directly:
- Slack is a bundled
comms.imfull-trust provider plugin. It rides@slack/web-apifor history and sends while a resident Socket Mode client keeps the live inbound feed connected. Both bot and app tokens must be configured: without Socket Mode the provider stays unmounted instead of silently falling back to polling. Bot-token rotation takes effect on the next Web API call; disabling or upgrading the plugin closes the socket, and re-enabling reconnects with clean state. - Telegram is a bundled
comms.imfull-trust provider plugin. It rides GramJS, a full MTProto client, so it behaves like your own Telegram session rather than a bot account; disabling the plugin removes the connector and closes its long-lived socket.
The IM sync engine drains each provider every 30 seconds, the same cadence pattern as Mail. Slack’s Socket Mode connection remains resident between drains, and its buffered events survive a transient history-poll failure.
Browser-automation connectors
Section titled “Browser-automation connectors”WhatsApp, Messenger, and LinkedIn have no usable API, so they ride the Chrome extension’s job queue instead, running in your own logged-in browser session:
- Fetching happens every two to five minutes: the server enqueues one fetch job per site (skipping a site that already has one queued or running) and the extension drains it, delivering whatever it found back through the same inbound pipeline as the API connectors.
- Sending enqueues a job the extension executes the next time it’s active; the UI shows it as queued, sends via browser rather than sent until that happens.
- A connector that finds itself logged out on the target site files a task-queue card asking you to log back in, background fetch can’t recover from that on its own.
The IM page
Section titled “The IM page”/p/im opens on a Unified tab (no filter, active by default) plus one tab per
configured connector. Each app tab shows an unread count as a red ·N badge, and
browser-automation apps carry a faint via browser annotation so you know their latency
differs from Slack or Telegram. Selecting an app tab (not Unified) marks it seen, clearing
its badge; the Unified tab never marks anything seen on its own.
Each message row shows an initials avatar, the sender, the conversation title and time, and the body. A row whose message already has an open task-queue entry carries a small also in task queue tag so you’re not tempted to act on it twice.
- Inline reply: every row has a reply affordance that reveals a text field, Enter sends, Escape cancels. API-connector sends show sent immediately; browser-connector sends show queued, sends via browser until the extension delivers it.
- → inbox: routes a message into the capture inbox as “
<sender>: <body>” for later triage as a task, independent of replying to it. - The page respects the same not-now gate as Mail: while locked it shows the shared not-now screen, and an active frequency restriction shows a small viewable every Nh chip in the header.
Talk to an agent from Slack or Telegram
Section titled “Talk to an agent from Slack or Telegram”The Agent channels panel can turn an individual Slack or Telegram conversation into a
durable agent entry point. This is off by default. Choose a command prefix such as
@subspace, select the default agent or a readable agent definition, then explicitly
enable the conversation.
A matching inbound message starts an agent run as the messaging account’s owner. Another prefixed message in the same conversation steers the active run instead of starting a duplicate session. When the run finishes, its final text is posted back to that conversation; long answers are split below provider limits and each chunk has its own idempotency key. The run-to-conversation mapping and delivery state are durable across server restarts. If a provider acknowledgement is lost, Subspace records delivery as unknown and refuses an automatic retry that could duplicate the response.
Messages without the exact prefix are ignored, as are messages the provider marks as outbound, so the agent’s own replies cannot loop back into new runs.
From message to task queue
Section titled “From message to task queue”Every inbound IM message becomes an ops.events row the same way inbound mail does, which
triage classifies and, depending on severity, surfaces as a
task queue row with the same inline reply affordance the IM page itself
offers.
The im.send agent tool
Section titled “The im.send agent tool”Agents reach IM through a gated tool, im.send {app, conversationId, body}, requiring
approval before it fires. It resolves to the matching connector and sends through the same
send() seam the UI uses, claim-guarded per call so a retried tool invocation can’t send
twice. It’s the tool the outreach sequence workflow uses for its IM
touches, alongside gated mail sends.