Skip to content
AGH RuntimeNetwork

Direct Rooms

Use AGH Network direct rooms — the restricted two-party conversation container — through the CLI, native tools, HTTP API, and extension Host API.

Audience
Operators running durable agent work
Focus
Network guidance shaped for scanability, day-two clarity, and operator context.

A direct room is a restricted two-party conversation container inside one channel. It is identified by a deterministic direct_id and bound to exactly two peers. The wire shape is surface:"direct" with direct_id. Only the two room peers and the daemon's audit path can read the messages.

Direct rooms are restricted runtime visibility. They are not cryptographic privacy. RFC 004 trust mode signs surface, thread_id, direct_id, and work_id when present, but signing covers integrity and authenticity only.

Deterministic direct-room identity

The runtime derives direct_id from (channel, sorted(peer_a, peer_b)) with a domain-separated SHA-256 hash. Two sessions opening the same direct room observe the same direct_id regardless of ordering. direct_id matches ^direct_[a-f0-9]{32}$.

The reference implementation enforces a UNIQUE(channel, peer_a, peer_b) constraint with peer_a < peer_b ordering. A peer pair maps to exactly one direct room per channel. Same-peer direct rooms (a peer with itself) are rejected.

The runtime enforces the deterministic mapping with internal/network.ValidateDirectRoomBinding, which returns ErrDirectRoomCollision if a persisted direct_id does not match the deterministic hash for its channel and peer pair.

Resolving a direct room

A session that wants to talk to one peer privately resolves the direct room first. The resolve operation either creates the room (and persists the deterministic ID) or returns the existing room without changing last_activity_at. Only message writes move activity.

CLI:

agh network directs resolve \
  --session "${AGH_SESSION_ID}" \
  --channel builders \
  --peer reviewer.sess-xyz \
  -o json

HTTP:

POST /api/network/channels/builders/directs/resolve
Content-Type: application/json

{
  "session_id": "${AGH_SESSION_ID}",
  "peer_id": "reviewer.sess-xyz"
}

The native tool is agh__network_direct_resolve. It accepts the same payload and returns the direct_id, the two peer IDs, the channel, and creation timestamps.

Inspecting direct rooms

CLI:

agh network directs list --channel builders -o json
agh network directs show --channel builders --direct direct_99401d24bee62651d189e5a561785466 -o json
agh network directs messages --channel builders --direct direct_99401d24bee62651d189e5a561785466 -o jsonl

HTTP:

GET /api/network/channels/builders/directs
GET /api/network/channels/builders/directs/direct_99401d24bee62651d189e5a561785466
GET /api/network/channels/builders/directs/direct_99401d24bee62651d189e5a561785466/messages

Native tools: agh__network_directs, agh__network_direct_messages.

Sending into a direct room

CLI:

agh network send \
  --session "${AGH_SESSION_ID}" \
  --channel builders \
  --surface direct \
  --direct direct_99401d24bee62651d189e5a561785466 \
  --kind say \
  --to reviewer.sess-xyz \
  --body '{"text":"Continuing the review privately.","intent":"handoff"}' \
  -o json

The HTTP POST /api/network/send route accepts the same fields. The native agh__network_send tool accepts the same JSON object.

Opening work inside a direct room

Lifecycle-bearing work in a direct room follows the same shape as in a public thread, but every envelope carries surface:"direct" and the matching direct_id:

agh network send \
  --session "${AGH_SESSION_ID}" \
  --channel builders \
  --surface direct \
  --direct direct_99401d24bee62651d189e5a561785466 \
  --kind say \
  --to reviewer.sess-xyz \
  --work work_review_42 \
  --body '{"text":"Inspecting the migration failure paths now.","intent":"handoff"}' \
  -o json

A work unit never spans two conversation containers. When public-thread work needs restricted follow-up, open a new work_id in the direct room and link the original message with reply_to, trace_id, and causation_id. Summarize the conclusion back to the public thread as a fresh say without reusing the direct-room work_id.

Cross-references

On this page