Skip to content

Feature proposal: terminal group/metadata fields + list_siblings discovery tool for multi-agent awareness #432

Description

@klabulan

Problem

Building a real product on top of CAO (harness-control, a multi-agent session manager), we've hit a genuine gap: an agent running in a CAO-managed terminal has no way to discover sibling terminals or message them, outside of the supervisor-dispatched handoff/assign flow.

Concretely: send_message is granted to our agent profile via cao-mcp-server, but per its own documented semantics it only works if the agent was handed a receiver_id (via a blocking [CAO Handoff] message or a non-blocking assign callback) or has a caller_id set (only true for handoff/assign-created terminals). A terminal created directly via POST /sessions (the flow any CAO consumer NOT using the supervisor/worker pattern relies on) has no caller_id and was never handed a receiver_id -- send_message is present but has nothing to target.

There's also no discovery primitive at all -- no MCP tool or API endpoint lets a running agent ask "what other terminals exist, and which of them are relevant to me." TerminalModel itself has no metadata/tags field a consumer could use to organize terminals into meaningful groups (e.g., "these three terminals are working on the same project").

We believe this is a real gap for any CAO consumer building multi-agent products outside the strict supervisor/worker shape, not something specific to our own product.

Proposal

Two additions:

1. group and metadata fields on TerminalModel

  • group: JSON (nullable) -- an ordered array of strings, general-to-specific (e.g. ["tenant_1", "project_5", "folder_12"]). Consumers decide what the levels mean; CAO just stores and does ordered-prefix matching. Settable at create_session time; updatable later via a new PATCH /terminals/{id}/group-style endpoint (important for consumers whose own grouping can change after a terminal already exists -- e.g. a folder gets reassigned to a different project in our own product).
  • metadata: JSON (nullable) -- free-form, no fixed schema, purely descriptive ("what is this terminal doing"). Settable at creation and updatable by the running agent itself via a new MCP tool.

2. list_siblings MCP tool (+ matching API endpoint)

  • Resolves the calling terminal's own identity via the same mechanism send_message/handoff already use (terminal's own env-provided identity, never a client-supplied "who am I" claim).
  • Looks up the caller's own group.
  • Takes an optional depth parameter: how many leading elements of group must match to be included in results. Server clamps depth to [1, len(caller_group)] -- can never be 0 (an unscoped, all-terminals-on-the-server query), and can never exceed the caller's own group length.
  • Returns {id, group, metadata} for every OTHER terminal whose group[0:depth] == caller_group[0:depth] AND depth <= len(sibling_group) (a sibling with a shorter group than the requested depth is excluded from that comparison rather than causing an index error or an undefined partial match).
  • A terminal with no group set participates in no discovery (neither finds others nor is found), rather than matching everything or crashing.

Why this shape

  • Keeps CAO domain-agnostic -- it never needs to understand what "project" or "folder" or "tenant" means, just ordered-array-prefix matching. Consumers own the semantics.
  • group[0] naturally becomes a hard multi-tenancy boundary for any consumer that sets it to a tenant/workspace id, since a caller can only ever compare against ITS OWN group[0], never an arbitrarily-requested one -- this falls out of the design rather than requiring CAO to understand tenancy.
  • Extensible without a schema migration -- more hierarchy levels, or (later) a real ACL/policy layer layered on top of the depth-prefix match as an additional filter, both fit without breaking existing consumers.

Known open questions / non-goals (flagging rather than hand-waving)

  • v1 doesn't include any deny/allow policy beyond group-prefix matching -- a consumer wanting finer-grained visibility (e.g. "visible to my project but not this specific other terminal") would need to build that on top for now.
  • No revocation/expiry semantics for group/metadata -- they're just current-state fields.
  • We haven't verified whether CAO's existing /terminals/{receiver_id}/inbox/messages mechanism is the right delivery path once a sibling is discovered via this tool, or whether list_siblings should return enough for the agent to just call the existing send_message tool directly -- open to whichever is more consistent with CAO's own architecture.

Our own validation plan if this has maintainer appetite

Per how we approached #382/#388: happy to build this against CAO's own dev environment and test suite, validate it end-to-end against our own real downstream product (harness-control), and submit a PR with regression tests -- flagging here first per your own contribution norms before writing code, since this is a real (if small) schema addition, not a bugfix.

Would appreciate a read on whether this fits CAO's own direction before we invest in a PR.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions