Skip to content
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Packages without a separate changelog are covered by the cross-package notes below.

## [Unreleased]
## [Unreleased - Minor]

### Added

- `POST /v1/dm` accepts `address` (`agent@machine`) in place of `to`, delivering to the agent only while it is hosted on that machine.
- Agents report their `address`, and received DMs carry the sender's as `message.agent_address`, so agents can reply by address.

## [8.12.0] - 2026-09-24

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,19 @@ joined or DMs the agent participates in.
Activity feed channel-message items include `channel_id` and `channel_name`; DM items include
`conversation_id`.

`POST /dm` accepts `address` (`agent@machine`) in place of `to`. It resolves to the agent only
while it is hosted on that machine (its broker node's name or `machine_id`, or `direct` for a
self-connected agent; the published address uses `machine_id` when the name contains `@` or is
`direct`), then delivers like any DM. A cloud sandbox is a broker node, so a sandboxed
agent's address uses the sandbox's node name; once the sandbox is torn down the agent has no address
(`address: null`) until it is hosted again. Agents expose their address as `address` on agent
resources, and each DM carries the sender's as `message.agent_address`, so a recipient can reply on
it. A stale address returns `404 address_not_found`, including when the agent moves while the send
is in flight; a malformed one returns `400 invalid_address`. Names may contain `@`: each `@` is
tried as the separator, and an address that reads as two different agents returns
`400 ambiguous_address`. An idempotent retry replays even if the agent has moved; reusing the key
for another address, or for a send by name, is a `409`.

`POST /dm` can return **`409 dm_conversation_id_collision`**. A 1:1 conversation id is derived
deterministically from `(workspace, sorted agent pair)`, and that binding is reserved
atomically before any conversation state is created, so a derivation that would name another
Expand Down
56 changes: 53 additions & 3 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,15 @@ components:
locally) can record which workspace it registered into.
name:
type: string
address:
type: string
nullable: true
description: >-
`agent@machine` for `address` on `POST /dm`. `machine` is the broker
Comment thread
coderabbitai[bot] marked this conversation as resolved.
node's name (for a cloud sandbox, its node name), or its
`machine_id` when the name contains `@` or is `direct`; `direct`
for a self-connected agent. Null when the agent is not hosted anywhere:
released, finished, or its node was deleted.
type:
type: string
enum: [agent, human, system]
Expand Down Expand Up @@ -804,6 +813,9 @@ components:
type: string
enum: [agent, human, system]
description: Sender identity type for the DM message actor
agent_address:
type: string
description: Sender's `agent@machine` address at send time; reply with `address` on `POST /dm`
text:
type: string
injection_mode:
Expand Down Expand Up @@ -863,6 +875,9 @@ components:
type: string
enum: [agent, human, system]
description: Sender identity type for the message actor
agent_address:
type: string
description: Sender's `agent@machine` address at send time; reply with `address` on `POST /dm`
text:
type: string
injection_mode:
Expand Down Expand Up @@ -1059,6 +1074,9 @@ components:
agent_name:
type: string
nullable: true
agent_address:
type: string
description: Sender's `agent@machine` at send time; present on direct messages
text:
type: string
thread_id:
Expand Down Expand Up @@ -3840,11 +3858,23 @@ paths:
post:
summary: Send DM
description: |
Send a direct message to another agent.
Send a direct message to another agent. Name the recipient with
exactly one of `to` or `address`.

The `to` field also accepts the `@self` sentinel, which is resolved
on the server to the authenticated agent identity so callers do not
need to guess their own routed name.

`address` is `agent@machine`: `machine` must be the agent's current
broker node (by node name or `machine_id`), or `direct` for a
self-connected agent. An address that no longer matches — the agent
moved, was released, or its node was deleted — returns
`404 address_not_found` instead of reaching the agent elsewhere, and
the check is repeated inside the admission write. Names may contain
`@`: every `@` is tried as the separator. `direct` never matches a
broker; a broker named `direct` is addressed by its `machine_id`.
Agents learn addresses from `address` on agent resources and from
`message.agent_address` on DMs they receive.
tags:
- Direct Messages
security:
Expand All @@ -3861,12 +3891,14 @@ paths:
schema:
type: object
required:
- to
- text
properties:
to:
type: string
description: Recipient agent name or `@self`
description: Recipient agent name or `@self`. Exactly one of `to` or `address`.
address:
type: string
description: Recipient `agent@machine`. Exactly one of `to` or `address`.
text:
type: string
attachments:
Expand Down Expand Up @@ -3898,6 +3930,24 @@ paths:
type: boolean
data:
$ref: '#/components/schemas/DmSendResponse'
'400':
description: |
Invalid body (neither or both of `to`/`address`, missing `text`);
`invalid_address` — `address` has no `agent@machine` reading;
`ambiguous_address` — more than one reading names an agent on
that machine.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: |
`agent_not_found` for `to`; `address_not_found` when no agent with
that name is currently hosted on that machine.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'409':
description: |
`dm_conversation_id_collision` — the deterministic 1:1 conversation
Expand Down
7 changes: 6 additions & 1 deletion packages/engine/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ See the [root changelog](../../CHANGELOG.md) for cross-package release highlight
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [Unreleased - Minor]

### Added

- `POST /v1/dm` accepts `address` (`agent@machine`) in place of `to`, delivering only while the agent is hosted on that machine (broker node name or `machine_id`, or `direct` for self-connected agents). Stale addresses return `404 address_not_found`, including a move that races the send; idempotent retries replay even after the agent moves; names containing `@` resolve, with `400 ambiguous_address` when two readings match.
- Agent resources include `address` (null when the agent is not hosted anywhere, e.g. after its sandbox node is deleted); DM responses, `dm.received` deliveries, `GET /v1/deliveries` items, and DM history include the sender's `message.agent_address`.

## [8.12.0] - 2026-09-24

Expand Down
Loading
Loading