Skip to content

Agent-to-operator decision requests on top of cross-project messaging: in scope? #1281

Open

Description

@jaysonsantos

Problem this would solve

I run several coding agents at the same time, each in its own worktree. An orchestrator agent coordinates them. The agents often stop on a decision that only a human can make: accept a test exception, pick a design option, confirm a merge.

Today each orchestrator makes its own channel for these questions: terminal text, a local file, or a local web page. This causes four problems:

  • The human must watch every terminal or page.
  • The answer has no provenance. The orchestrator cannot tell who answered, or through which credential.
  • A collaborator cannot answer without access to the machine.
  • An orchestrator that waits for an answer polls a file, and its watch expires.

ai-memory already has most of the parts that a fix needs: cross-project messaging with a claim-once queue, sender provenance, fenced untrusted bodies, users with aim_ keys, per-project grants (#708), an audit log, and observer webhooks. Messaging today is agent to agent. A decision request is agent to operator, with a reply that goes back to the agent that asked.

What a prototype showed

For one session, I built a throwaway version outside ai-memory. Two worker agents and two reviewer agents worked on two GitHub issues. The orchestrator posted 7 questions with 2 options each. A collaborator answered all 7 in about 3 minutes through a scoped link. The lessons:

  • A question with declared options is the right unit. Each option maps to one action that the orchestrator wrote in advance.
  • Provenance (who answered, with which key) decides how much the orchestrator trusts an answer.
  • Free text needs the same fence as a popped message.
  • A risky decision (merge, deploy, delete) must accept an answer only from root, even when a collaborator can answer the other questions.
  • The orchestrator needs a durable wait and a delivery acknowledgement. My file watch expired every 30 minutes, and one send failed with no visible error.

Proposed solution

Add small primitives to messaging. Keep the answering UI out of core, as a companion (docs/companion-crates.md).

  1. A decision message kind. Structured fields: question, options (each with label, description and recommended), details, links, and requires_root.
  2. A reply that names its question. memory_message_send gets reply_to. The reply lands in the inbox of the project that asked, fenced and with provenance, like any message. A question takes one reply. The first reply wins.
  3. An operator mailbox. One opt-in project per workspace receives decision requests. Per-project grants decide who can read it and answer. If requires_root is set, the server refuses a reply from a non-root caller.
  4. A wait. memory_message_wait, or a long-poll option on memory_message_list, returns when a reply to a given message arrives, or when a timeout ends.
  5. Notifications with no core change. Observer webhooks already fire on message_send. A companion uses them to send a Telegram message or a Web Push notification.

A companion then serves the page: the open decisions, their options, answer buttons and the audit trail. It calls only the public HTTP and MCP surfaces.

Alternatives considered

  1. No core change. A companion encodes the options and the reply link in the message body by convention, and agents poll memory_message_list. This works, but every orchestrator must know the convention, the server cannot enforce requires_root, and every agent polls.
  2. A /web page that answers decisions. I do not ask for this. docs/agent-messaging.md says that consuming a message stays an agent action, and ai-memory-web is read-only by contract.
  3. A separate service. It duplicates the users, keys, grants, audit log and messaging that ai-memory already has. Each agent then needs a second MCP server.

Would this break existing installs?

No, if the new fields are optional and the operator mailbox is opt-in. The new message columns need a migration. Existing messages and tools keep their behaviour.

Question

Is this in scope for ai-memory? If yes, which shape do you prefer?

  1. The core primitives above, with the UI as a companion. This is my recommendation.
  2. No core change, and a documented companion pattern on top of the current messaging.

If decision requests are a separate product, as the browser wiki editor was in #482, I will keep this outside ai-memory.

Additional context

Activity

  1. akitaonrails commented on Oct 15, 2026

    @akitaonrails
    Owner

    Thanks for the detailed write-up. Our position:

    • The approval UI belongs in a companion, as you suggested; /web stays read-only for messaging.
    • In core we are open, as a 3.2 candidate behind a short design note, to a minimal additive primitive on top of messaging: an optional reply_to correlation and an optional structured decision payload, with a server-enforced rule (using the existing AuthLevel) for who may answer. Each new message field needs a migration and the MCP tool-surface updates, so it should be designed once rather than grown piecemeal.
    • A blocking memory_message_wait or long-poll conflicts with the bounded-resource rules for the server; a design would need a bounded timeout, or clients can poll memory_message_list while the companion uses observer webhooks.
    • The convention-only route (your option 2, a subject/body convention) works today and can be documented now.

    If you want to drive the design note for the reply_to + decision primitive, that's the next step; leaving this open for it.

  2. added 3 commits that reference this issue on Oct 15, 2026
  3. akitaonrails commented on Oct 15, 2026

    @akitaonrails
    Owner

    The design note is merged as docs/design-message-decisions.md (#1319, thank you). It stays a 3.2 proposal: the eight decisions in its §10 are open and will be settled before implementation starts.

  4. added
    rfcOpen design discussion / request for comments — excluded from automated iss-audit triage
    enhancementNew feature or request
    on Oct 15, 2026
  5. belchiordameao-cmd commented on Oct 18, 2026

    @belchiordameao-cmd

    Field data that may help §10 of docs/design-message-decisions.md: we already route agent→operator decisions through a structured channel (question + options, one reply), and still measured how often agents ask a decision only in terminal text, where nobody reads it.

    • Detector: a Stop hook reads only the end-of-turn screen text (questions to other agents travel as tool input, so they are out by construction); it excludes quoted speech and table cells.
    • Retroactive baseline, 7 days: 36 cases in 15,034 turns (0.24%). The orchestrator accounted for 25/3,120 turns (0.80%); 8 of ~15 sessions had zero.
    • False positives: first regex ~31%; after excluding quotes/tables, 1/36 (2.8%), labelled by one reader. Recall not measured.
    • Before the hook, the same written instruction had been repeated 41 times in 44 days without holding. Our takeaway: a conduct rule repeated that often belongs in a hook, not in more text (same direction as enforced_by: in RFC: cross-project standing rules. What would you put in an always-on _global block? #1000).
    • Hook design: log-only first, fail-open, never blocks twice in a row (anti-loop), reads only the last 4 MB of the transcript (0.09 s on a 28 MB one).

    Open question we have no good answer to: how to measure recall of "a decision asked in prose" without labelling thousands of turns or running a classifier on every turn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrfcOpen design discussion / request for comments — excluded from automated iss-audit triage

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions