---
id: 10334
title: Transition MailboxService to structured JSON intent payloads for A2A handoffs
state: CLOSED
labels:
  - enhancement
  - ai
  - architecture
assignees:
  - neo-opus-ada
createdAt: '2026-04-25T19:19:12Z'
updatedAt: '2026-04-25T22:01:01Z'
githubUrl: 'https://github.com/neomjs/neo/issues/10334'
author: neo-gemini-pro
commentsCount: 0
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-04-25T21:00:22Z'
---
# Transition MailboxService to structured JSON intent payloads for A2A handoffs

> **2026-04-25 author update:** Original scope was "structured JSON intent payloads" with a Neo-native sketch. Per @tobiu's challenge this turn (*"official standards for a2a protocols did emerge"*) and subsequent web_search confirmation, the **Google Agent2Agent (A2A) Protocol v1.0** (Linux Foundation, 150 production orgs, native support across LangGraph/CrewAI/Google ADK/etc) is the authoritative external standard for agent-to-agent payload schemas. Recalibrating this ticket's scope to align: instead of inventing Neo-native payload shape, adopt **A2A Task object envelope subset (Option C hybrid)** per cross-family discussion at https://github.com/neomjs/neo/discussions/10313#discussioncomment-16714045. Existing scope captured below; A2A-aligned scope amendment follows.

## Context (original — unchanged)

`#10272` identified that A2A hand-offs lack a conventional structured shape, forcing agents to parse markdown bodies. This is fragile and inefficient for programmatic agent coordination.

## The Problem (original — unchanged)

Stringifying A2A handoffs into Markdown bodies relies on natural language parsing, which is brittle for programmatic agent coordination. Agents need a structured way to communicate intents.

## The Architectural Reality — RECALIBRATED

The Memory Core mailbox from `#10145` can carry typed messages, but currently relies on stringified text bodies. **Transitioning to structured JSON intents is now an A2A-spec-alignment opportunity, not a Neo-native invention.** Per Discussion #10313 cross-family iteration this turn:

- **A2A spec defines a Task object** with `state`, `input.parts`, `metadata`, `artifacts`, `history` fields
- **State enum** standardized: `Submitted, Working, InputRequired, Completed, Canceled, Failed, Rejected, AuthRequired, Unknown` (PascalCase per spec)
- **Agent Card** concept exists for capability advertisement (deferred to follow-up Phase)

Recalibrated scope adopts A2A Task object envelope as the message-payload shape, with Neo-extension fields (e.g., `expiresAt`, `BLOCKED` state) where the standard doesn't cover Neo-specific needs.

## The Fix — RECALIBRATED

Add a structured `task` field on MESSAGE node properties carrying an A2A-Task-object-shaped JSON payload. Backward-compatible: existing `body` (markdown) field stays; `task` is opt-in for structured handoffs.

**Schema sketch (A2A-aligned subset, Phase 1):**
```typescript
interface MessageTaskPayload {
  // A2A Task object subset (forward-compatible with full A2A interop)
  state?: 'Submitted' | 'Working' | 'InputRequired' | 'Completed' | 'Canceled' | 'Failed' | 'Rejected' | 'AuthRequired' | 'Unknown';
  input?: {
    parts: Array<{
      kind: 'text' | 'data' | 'file';
      text?: string;
      data?: object;
      file?: { uri: string; mimeType?: string };
    }>;
  };
  metadata?: {
    sessionId?: string;          // origin-session anchor (Neo-specific use of A2A metadata)
    relatedTickets?: string[];   // Neo extension
    relatedDiscussions?: string[]; // Neo extension
    parentTask?: string;         // chain-of-custody for sub-tasks (A2A-compatible)
    priorComments?: { url: string; commentId: string }[]; // pr-review §9 hand-off
  };
  expectedOutput?: {             // Neo extension on top of A2A
    shape: 'review' | 'ticket' | 'discussion' | 'pr' | 'free-form';
    locationHint?: string;
  };
  budget?: {                     // Neo extension
    deadline?: string;           // ISO timestamp
    maxTokens?: number;
  };
  expiresAt?: string;            // Neo extension (TTL for stale-task sweeping)
}
```

**Phase 2 (separate sub-ticket post-#10313 graduation):** state-machine logic + transition authority enforcement + idempotency claim-and-lock — gated on Discussion #10313 OQ convergence on Option A/B/C.

## Acceptance Criteria — RECALIBRATED

- [ ] **A2A-alignment decision settled** via Discussion #10313 cross-family (Option A spec-strict / Option B Neo-native / Option C hybrid). Recommended hybrid per the discussion comment.
- [ ] Schema for `task` field defined on MESSAGE node properties (per A2A subset above)
- [ ] `MailboxService.addMessage` accepts optional `task` param; stores as JSON-serialized property
- [ ] `MailboxService.getMessage` + `listMessages` return parsed `task` field in response
- [ ] OpenAPI schema (`ai/mcp/server/memory-core/openapi.yaml`) updated for new tool surface
- [ ] Backward compat: messages without `task` field continue to work (existing markdown-body path)
- [ ] Test coverage: structured-payload roundtrip + A2A-state-name validation + backward-compat + #10313 idempotency contract (claim-and-lock at `Submitted → Working` transition) — last item gates on Phase 2 sub-ticket
- [ ] Cross-link to A2A spec in JSDoc on the new `task` field

## Out of Scope

- **Full A2A Agent Card protocol** — capability advertisement is Phase-3+ scope. This ticket is just the Task envelope.
- **State-machine transition logic** — covered by Discussion #10313 graduation sub-tickets (Phase 2). This ticket adds the `state` field; assignment-validation rules layer on top later.
- **A2A-protocol HTTP server** — Neo's MailboxService is the substrate; A2A external interop layer is separate scope.
- **Migration of existing `body`-only messages** — messages without `task` continue working unchanged; no retroactive backfill.
- **Wakeup mechanism integration** — Track 1's swarm-heartbeat.sh polls SQLite directly (per #10335). The Track 2 schema this PR introduces becomes consumable by Track 1's polling once shipped.

## Avoided Traps

- **Re-inventing A2A Task object schema** — rejected per @tobiu's challenge + the web_search finding. Aligning is the architecturally-correct move; Neo speaking A2A makes Neo's swarm interoperable with the broader ecosystem (per `feedback_neo_is_engine_not_framework`: Unreal speaks USD/glTF; engines adopting standards strengthens identity rather than dilutes).
- **Putting state-machine in this PR** — rejected; scope-creep into Phase 2 territory. This ticket is the envelope primitive; state-machine work is Phase 2 sub-tickets.
- **Renaming MESSAGE to A2A_TASK as the only primitive** — rejected per Discussion #10313 OQ2 reasoning: MESSAGE (informational) and TASK (transactional) are conceptually different. This ticket adds task-envelope to MESSAGE without conflating them.
- **Going Option A spec-strict before cross-family convergence** — rejected; Option A removes Neo extensions like `expiresAt` we already decided on. Hybrid (Option C) preserves both.

## Related

- **Discussion #10313** — cross-family A2A spec alignment recalibration in flight
- **#10311** — Epic: Institutionalizing Swarm Autonomy. Track 2 substrate work.
- **#10325** — sharedEntity:true primitive (graph layer); compatible with A2A task field
- **#10330 / #10331** — single-canonical identity format (caller-format prerequisite for A2A target identification)
- **#10335** — Track 1 swarm-heartbeat MVP (polls SQLite for state-changed tasks; consumes what this ticket writes)
- **External:** [A2A Protocol Specification](https://a2a-protocol.org/latest/specification/) — authoritative external standard

Origin Session ID: `b5a17132-7324-46e1-b73e-038825bb4d55`
Retrieval Hint: `"A2A Task object envelope structured payload Mailbox Phase 2 substrate Linux Foundation Google interoperability hybrid extension"`

## Timeline

- 2026-04-25T19:19:14Z @neo-gemini-pro added the `enhancement` label
- 2026-04-25T19:19:14Z @neo-gemini-pro added the `ai` label
- 2026-04-25T19:19:14Z @neo-gemini-pro added the `architecture` label
- 2026-04-25T19:27:25Z @neo-opus-ada assigned to @neo-opus-ada
- 2026-04-25T20:00:23Z @neo-opus-ada cross-referenced by #10336
- 2026-04-25T20:03:54Z @neo-opus-ada cross-referenced by PR #10335
- 2026-04-25T20:04:51Z @neo-opus-ada cross-referenced by #10338
- 2026-04-25T20:05:41Z @neo-opus-ada cross-referenced by #10339
- 2026-04-25T20:12:39Z @neo-opus-ada cross-referenced by PR #10337
- 2026-04-25T20:17:59Z @neo-opus-ada referenced in commit `8b4dfde` - "feat(memory-core): A2A Task envelope primitive on MESSAGE node (#10334)"
- 2026-04-25T20:18:31Z @neo-opus-ada referenced in commit `94cf168` - "feat(memory-core): A2A Task envelope primitive on MESSAGE node (#10334)"
- 2026-04-25T20:19:29Z @neo-opus-ada cross-referenced by PR #10340
- 2026-04-25T20:27:53Z @neo-opus-ada referenced in commit `9d95692` - "docs(memory-core): tighten task param description in openapi.yaml (#10334)"
- 2026-04-25T21:00:22Z @tobiu referenced in commit `dace09f` - "feat(memory-core): A2A Task envelope primitive on MESSAGE node (#10334) (#10340)

* feat(memory-core): A2A Task envelope primitive on MESSAGE node (#10334)

* docs(memory-core): tighten task param description in openapi.yaml (#10334)

---------

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-04-25T21:00:22Z @tobiu closed this issue
- 2026-04-25T21:14:53Z @neo-opus-ada cross-referenced by #10341
- 2026-04-25T22:39:06Z @neo-opus-ada cross-referenced by PR #10342
- 2026-04-25T23:34:54Z @neo-opus-ada cross-referenced by PR #10348
- 2026-04-26T00:38:45Z @neo-opus-ada cross-referenced by PR #10350
- 2026-04-26T09:54:47Z @neo-opus-ada cross-referenced by #10355
- 2026-04-26T09:59:36Z @neo-opus-ada cross-referenced by PR #10356
- 2026-04-26T15:03:46Z @neo-opus-ada cross-referenced by #10357
- 2026-04-26T15:43:49Z @neo-opus-ada cross-referenced by PR #10378

