From 2c3bcd1ec23dd75cd9680e824404ad1e7d5f7964 Mon Sep 17 00:00:00 2001 From: Alassane <79718216+yattdev@users.noreply.github.com> Date: Sun, 19 Jul 2026 17:17:15 -0400 Subject: [PATCH] docs: document cross-task agent communication and MCP tools (#1793) --- docs/public/agent-communication.md | 195 +++++++++++++++++++++++++++++ docs/public/automation-and-mcp.md | 2 +- docs/public/coordination.md | 2 + docs/public/coverage.json | 2 +- docs/public/index.md | 1 + docs/public/meta.json | 1 + 6 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 docs/public/agent-communication.md diff --git a/docs/public/agent-communication.md b/docs/public/agent-communication.md new file mode 100644 index 0000000000..2a486a9d65 --- /dev/null +++ b/docs/public/agent-communication.md @@ -0,0 +1,195 @@ +--- +title: "Agent Communication" +description: "How agents send messages across tasks, receive replies, and run multi-turn negotiations — with built-in Kandev MCP tools and a worked contract-negotiation example." +--- + +# Agent Communication + +Agents running in separate Kandev tasks can talk to each other directly. One agent sends a structured prompt to another task; the receiving agent processes it as a normal turn and sends its reply back the same way. This closes the loop into a genuine two-way conversation without any additional infrastructure. + +Communication works across all relationship types — parent to child, child to parent, sibling to sibling, and tasks in entirely different workspaces or workflows ("projects"). The only requirement is that each agent knows the other task's full UUID. + +## Discovering task IDs + +An agent typically learns a peer's task ID in one of three ways: + +**From related tasks.** `list_related_tasks_kandev` returns parent, children, siblings, blockers, and blocked-by tasks for the current task. Each entry includes the full task UUID, title, lifecycle state, and associated pull requests — but not the workflow step (kanban column). To check which column a related task currently sits in, look it up with `list_tasks_kandev` on its workflow. + +``` +list_related_tasks_kandev() +# → { "children": [{ "id": "uuid-of-B", "title": "Frontend: Item list" }], ... } +``` + +**From a creation return value.** `create_task_kandev` returns the newly created task's ID. A coordinator that spawns subtasks immediately holds every child's UUID and can message them as soon as they start. + +**By browsing the board.** `list_workspaces_kandev` → `list_workflows_kandev` → `list_tasks_kandev` lets an agent walk the full board structure, including tasks in other workspaces. Because `message_task_kandev` accepts any task UUID, cross-project messaging (tasks in separate workflows or workspaces) works without special configuration. + +## Sending a message + +``` +message_task_kandev(task_id="", prompt="") +``` + +By default, the message goes to the task's primary session. Advanced callers can pass +`session_id` to target a specific session that belongs to that task, including a sibling +session on their own task. `spawn_session_kandev` returns `{task_id, session_id, state}` +when an agent starts an additional session on an existing task; pass the `session_id` field to `message_task_kandev`. + +Delivery behaviour depends on the target session's state at the moment of the call: + +| Target session state | What happens | Return value | +|---|---|---| +| Running or starting | Message is queued; delivered when the current turn ends | `"queued"` | +| Running or starting, direct parent uses `delivery_mode="interrupt"` | Kandev queues the message, then tries to interrupt the current turn and deliver it immediately | `"sent"` if interrupted and delivered immediately; otherwise `"queued"` | +| Idle (waiting for input, or turn just finished) | Message starts a new turn immediately | `"sent"` | +| Created (agent not yet started) | Agent is started with this message as its first prompt | `"started"` | +| Failed or cancelled | Error is returned | — | + +The `queued` outcome is normal and correct. It does not mean the message was dropped; it means the receiving agent will process it on its very next turn. Design your messages to be self-contained so that a small delay between delivery and processing does not matter. + +A direct parent task has one extra option for steering a child that is already running: +`delivery_mode="interrupt"`. Kandev first persists the message in the queue, then +attempts to cancel the child's current turn and deliver that queued message immediately. +If the interrupt cannot dispatch immediately, the message stays safely queued for the +normal turn-completion drain. This mode is intentionally restricted to the target task's +direct parent; any other sender that requests `interrupt` receives a hard error instead +of being silently downgraded to `queued`. + +## Receiving and replying + +The receiving agent processes the incoming message as an ordinary user turn. It can use any tool, read code, run tests, and reason freely before formulating its response. + +To reply, the agent calls `message_task_kandev` with the **originating task's ID** and the response text. That message becomes a new turn in the sender's conversation — closing the loop into a bidirectional exchange. + +``` +# Agent B, replying to Agent A after receiving a proposal: +message_task_kandev( + task_id="", + prompt="Acknowledged. I need created_at in the response too. ISO-8601 format preferred." +) +``` + +There is no persistent channel or shared socket between tasks. Each `message_task_kandev` call is discrete. The conversation thread is implicit — both tasks can recover context by reading back the relevant messages (see [Reading the thread](#reading-the-thread) below). + +## Reading the thread + +`get_task_conversation_kandev(task_id)` returns the message history for any task you know the ID of. Use it to: + +- verify what the other task agreed to, before starting implementation; +- resume context after a long pause; +- audit what was actually communicated. + +Pagination and filtering are available via `limit`, `before`, `after`, `sort`, and `message_types` parameters. `message_types=["message"]` returns every regular chat/prompt row — the same `message` type covers ordinary user turns and cross-task deliveries alike, so this filter alone does not isolate coordination traffic. To identify a message that arrived via `message_task_kandev`, check the returned message's `metadata.sender_task_id` (and `sender_task_title`): messages sent within the same task have no `sender_task_id`. Include `"tool_call"` in `message_types` to see tool outputs too. + +## Worked example — API contract negotiation + +Backend Task A and Frontend Task B need to agree on an API contract before they both start implementing. Neither task knows what the other needs; they resolve it through a short negotiation. + +### Setup + +Task A (backend) was created by a coordinator. The coordinator also created Task B (frontend) and passed both task IDs to each agent in their initial prompts. + +### The exchange + +```mermaid +sequenceDiagram + participant A as Agent A (Backend) + participant B as Agent B (Frontend) + A->>B: message_task_kandev(B, "Proposing GET /items → {id, name}. Does this cover your needs?") + Note over B: idle → "sent" (new turn) + B->>A: message_task_kandev(A, "Need created_at too, ISO-8601 format please.") + Note over A: running → "queued" (next turn) + A->>B: message_task_kandev(B, "Agreed: GET /items → {id, name, created_at} (ISO-8601). Starting impl.") + Note over A,B: Contract agreed — both implement + A-->>A: get_task_conversation_kandev(B) to confirm spec +``` + +### Agent A's actual tool calls (turn by turn) + +**Turn 1 — propose the contract:** + +``` +message_task_kandev( + task_id="frontend-task-uuid", + prompt=""" + I'm Agent A on the backend task (task ID: backend-task-uuid, repo: api, branch: feat/items-endpoint). + Proposing the following API contract for the /items endpoint: + + GET /items + Response: { "items": [{ "id": string, "name": string }] } + + Does this cover your frontend needs? Reply with any required changes or "agreed". + """ +) +# → "sent" (Task B was idle) +``` + +**Turn 2 — Task B's reply arrives as a new user turn in Task A's conversation:** + +``` +# Incoming turn (user message in A's conversation): +# "Need created_at too; ISO-8601 format preferred." + +# Agent A accepts and confirms: +message_task_kandev( + task_id="frontend-task-uuid", + prompt=""" + Agreed. Updated contract: + + GET /items + Response: { "items": [{ "id": string, "name": string, "created_at": string }] } + Format: created_at is ISO-8601 (e.g. "2024-01-15T10:30:00Z"). + + I'm starting implementation now. Let me know if anything changes. + """ +) +# → "queued" (Task B was running its own turn) +``` + +**Turn 3 — verify before writing tests:** + +``` +get_task_conversation_kandev( + task_id="frontend-task-uuid", + message_types=["message"], + sort="asc" +) +# Returns the full exchange; Agent A confirms the agreed spec before writing tests. +``` + +### What Agent B does + +Task B processes each incoming message as a normal turn. When it receives Agent A's proposal, it reads the current frontend component that consumes the API, identifies the missing field, and sends back a specific counter-proposal. When the agreement arrives, it proceeds to implement its API client with the agreed shape. + +## Good practices + +**Be self-contained.** Each message should include enough context for the receiving agent to act without looking up prior conversation history — task ID, repo, branch, current status, and what you expect in return. + +**One bounded question at a time.** Cross-task messages work best for specific, answerable requests ("does this schema cover your needs?"), not open-ended collaboration. Keep the negotiation short. + +**Verify before implementing.** After reaching agreement, read the other task's conversation with `get_task_conversation_kandev` to confirm the final state before writing code that depends on it. + +**Avoid loops.** Do not enter an infinite exchange. Agree on a clear stopping condition (e.g. "reply with 'agreed' or a specific counter-proposal") and implement after at most two or three rounds. + +**No secrets or large dumps.** Messages are coordination, not a code-delivery channel. Do not send credentials, private keys, or large file contents through cross-task messages. Reference files by path; share access via the repository, not the message. + +**Failed/cancelled tasks cannot receive messages.** If `message_task_kandev` returns an error, the target task is in a terminal state. Create a fresh task with `create_task_kandev` and start over. + +## Related built-in actions + +These tools complement cross-task communication for common coordination patterns. The full MCP surface is documented in [Automation and MCP](automation-and-mcp.md). + +| Tool | Use for | +|---|---| +| `message_task_kandev` | Send a prompt to any task by UUID | +| `get_task_conversation_kandev` | Read a task's message history (pagination, type filters) | +| `list_related_tasks_kandev` | Discover parent / child / sibling / blocker task IDs | +| `create_task_kandev` | Delegate work to a new subtask; returns the new task's ID | +| `spawn_session_kandev` | Start another session on an existing task; returns `{task_id, session_id, state}` — use the `session_id` field to message the new session directly | +| `move_task_kandev` | Hand off a task to the next workflow step with an optional prompt for the receiving agent | +| `create_task_plan_kandev` | Record an agreed implementation plan (both tasks can create/update their own plans) | +| `get_task_plan_kandev` | Read a task's plan — useful before messaging to share a structured proposal | +| `step_complete_kandev` | Signal that the current workflow step is done (task-mode only) | +| `ask_user_question_kandev` | Escalate to a human when agent negotiation cannot resolve a question | + +Related: [Coordinate Work](coordination.md), [Automation and MCP](automation-and-mcp.md), [Tasks and workflows](tasks-and-workflows.md). diff --git a/docs/public/automation-and-mcp.md b/docs/public/automation-and-mcp.md index 867e6a6f91..891ce20230 100644 --- a/docs/public/automation-and-mcp.md +++ b/docs/public/automation-and-mcp.md @@ -136,7 +136,7 @@ Task mode currently registers these tool groups: | Group | Available operations | |---|---| | Board lookups and task lifecycle | List workspaces, workflows, workflow steps, tasks, agents, and executor profiles; create, update, move, archive, or delete tasks; halt all live work on a direct child. This mode does not mutate workflows, profiles, or executors. | -| Coordination | Message a task or targeted session, spawn a named session on the current or another same-workspace task, and read task conversation. | +| Coordination | Message a task or targeted session, spawn a named session on the current or another same-workspace task, and read task conversation. See [Agent Communication](agent-communication.md) for delivery semantics, bidirectional reply patterns, and a worked example. | | User interaction | Ask a structured question when the current agent/session supports it. | | Plans | Create, get, update, and delete the current task plan. | | Walkthroughs | Show, get, and delete the task's code walkthrough. | diff --git a/docs/public/coordination.md b/docs/public/coordination.md index 55264bc176..c073e5d0de 100644 --- a/docs/public/coordination.md +++ b/docs/public/coordination.md @@ -145,6 +145,8 @@ Additional messaging boundaries: Use `get_task_conversation_kandev` to read a primary or explicit session conversation. It supports limits, before/after cursors, ascending or descending order, and message-type filters. Use `list_related_tasks_kandev` for the current or another same-workspace task to list its parent, direct children, siblings, stored blocker relationships, and associated GitHub pull requests. +Replies close the loop: the receiving agent calls `message_task_kandev` back with the originating task's ID, turning a one-way notification into a genuine bidirectional conversation. This enables multi-turn negotiations between agents — for example, agreeing on an API contract before both sides implement. See [Agent Communication](agent-communication.md) for delivery semantics, discovery patterns, and a worked negotiation example. + ## Wait for child tasks On the parent's current workflow step, configure **When Child Tasks Complete** to move next, previous, or to a selected step. It runs once when every active direct child is `COMPLETED`, `FAILED`, or `CANCELLED` and the parent has an active session. diff --git a/docs/public/coverage.json b/docs/public/coverage.json index 1e483cb32d..e5403ef85d 100644 --- a/docs/public/coverage.json +++ b/docs/public/coverage.json @@ -90,7 +90,7 @@ "id": "sessions-and-coordination", "audiences": ["user"], "stability": "stable", - "docs": ["sessions-and-review", "coordination", "automation-and-mcp"], + "docs": ["sessions-and-review", "coordination", "automation-and-mcp", "agent-communication"], "sources": ["apps/backend/internal/mcp/handlers/spawn_session.go", "apps/web/components/task/dockview-session-tabs.ts"], "tests": ["apps/backend/internal/mcp/handlers/spawn_session_test.go", "apps/web/components/task/dockview-session-tabs.test.ts"], "mcpTools": [ diff --git a/docs/public/index.md b/docs/public/index.md index 56e2b1d235..e9317894f4 100644 --- a/docs/public/index.md +++ b/docs/public/index.md @@ -15,6 +15,7 @@ Start with the page that matches the job in front of you: - [Tasks and workflows](tasks-and-workflows.md): create tasks, use plans, configure workflow steps, and understand the Office-only document and label boundary. - [Sessions and review](sessions-and-review.md): work with named sessions, chat, files, terminal, changes, preview, pull requests, and walkthroughs. - [Coordinate work](coordination.md): use parallel sessions, targeted messages, subtasks, dependencies, multiple repositories, and additional branches. +- [Agent communication](agent-communication.md): send cross-task prompts, receive replies, and negotiate contracts with built-in Kandev MCP tools. - [Agents and profiles](agents-and-profiles.md): configure agent CLIs, models, modes, permissions, environment, passthrough, and credentials. - [Executors](executors.md): choose local, worktree, Docker, SSH, or Sprites execution and understand the isolation boundary. - [Integrations](integrations.md): configure GitHub, GitLab, Jira, Linear, Sentry, and Slack for a workspace. diff --git a/docs/public/meta.json b/docs/public/meta.json index d3b2856ef1..f28bd92ef6 100644 --- a/docs/public/meta.json +++ b/docs/public/meta.json @@ -10,6 +10,7 @@ "tasks-and-workflows", "sessions-and-review", "coordination", + "agent-communication", "developer-tools", "---Configure Kandev---", "agents-and-profiles",