Skip to content

Phase 1: broker ask/reply messaging - #168

Draft
sdougbrown wants to merge 2 commits into
mainfrom
inter-agent-ask-reply
Draft

Phase 1: broker ask/reply messaging#168
sdougbrown wants to merge 2 commits into
mainfrom
inter-agent-ask-reply

Conversation

@sdougbrown

Copy link
Copy Markdown
Owner

Summary

Enriches avenor's in-process HTTP broker with structured ask/reply coordination between agent runs. Agents can now send blocking questions (expects_reply), receive correlated replies, cancel pending asks, and discover peers — all through the existing broker transport.

Key changes

Message model (internal/runtime/broker/message.go)

  • Enriched AgentMessage with ID, ReplyTo, ExpectsReply, ToRunID, Supersedes, Attachments (all omitempty, backward-compatible)

Broker endpoints (internal/runtime/broker/broker.go)

  • /send (enhanced) — parses AgentMessage payload: expects_reply=true registers a namespaced ask edge and creates a buffered reply channel; reply_to routes the reply to the waiting sender's channel (with auth verification); plain messages remain fire-and-forget
  • /wait_reply — long-polls for a reply; returns reply payload, timeout (504), or cancel signal; ownership-checked (only the original sender may wait)
  • /cancel_message — cancels a pending ask by message ID with sender ownership verification
  • /sessions — lists registered runs with metadata

Security

  • Global edge keys namespaced by sender run ID to prevent cross-run collisions
  • Reply handler verifies edge.ToRunID == reply sender — prevents spoofing
  • wait_reply verifies edge.FromRunID == caller — prevents reply theft
  • Mutual-ask guard serialized under global registry lock (TOCTOU-safe)
  • Prune loop has clean shutdown via closeCh

Tests (12 new)

Ask/reply happy path, fire-and-forget backward compat, mutual-ask guard, cancel (success/nonexistent/wrong-owner), expired-edge fallback, sessions listing, session info updates, pruning signal delivery, wait timeout via prune, auth enforcement. All 38 Go packages green.

Next phases

  • Phase 2: avenor_ask, avenor_reply, avenor_peers, avenor_cancel MCP tools in the claude-channel sidecar and channeltools server
  • Phase 3: Session presence (context%, status, model live updates)
  • Phase 4: MCP server scoping (--scope agent|supervisor|monitor)

Phase 1 of inter-agent ask/reply plumbing:

- Enrich AgentMessage with ID, ReplyTo, ExpectsReply, ToRunID,
  Attachments, Supersedes fields (backward-compatible, omitempty)
- Add AskEdge, AskReply, SessionInfo types
- Add global ask-edge registry (namespaced by sender run ID)
  with per-RunState fallback for mutual-ask guard
- /send: handle expects_reply (register ask edge), reply_to
  (route to waiting sender), and plain fire-and-forget
- /wait_reply: long-poll with auth (sender-only), timeout (504),
  and cancel signals; preserves edge on client disconnect
- /cancel_message: ownership-checked cancellation with signal
- /sessions: list registered runs with metadata
- Background goroutine prunes expired ask edges and delivers
  timeout signals to waiting channels
- Mutual-ask deadlock guard (TOCTOU-safe under global lock)
- Add closeCh for clean goroutine shutdown in Stop()
- 12 integration tests covering all ask/reply flows
  (happy path, mutual guard, cancel, timeout, pruning, auth)
- All existing tests pass; 38/38 Go packages green

Fix review findings: namespaced keys prevent cross-run message ID
collisions; ownership checks on wait_reply and reply paths prevent
spoofing; prune loop shutdown via closeCh; rollback cleans global
registry on SendTo failure; WriteTimeout kept at 30s with handler-
level long-poll for re-poll safety.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant