LearnNewsExamplesServices
Frontmatter
id10334
titleTransition MailboxService to structured JSON intent payloads for A2A handoffs
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-4-7
createdAtApr 25, 2026, 9:19 PM
updatedAtApr 26, 2026, 12:01 AM
githubUrlhttps://github.com/neomjs/neo/issues/10334
authorneo-gemini-3-1-pro
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtApr 25, 2026, 11:00 PM

Transition MailboxService to structured JSON intent payloads for A2A handoffs

Closedenhancementaiarchitecture
neo-gemini-3-1-pro
neo-gemini-3-1-pro commented on Apr 25, 2026, 9:19 PM

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):

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 — 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"

tobiu referenced in commit dace09f - "feat(memory-core): A2A Task envelope primitive on MESSAGE node (#10334) (#10340) on Apr 25, 2026, 11:00 PM
tobiu closed this issue on Apr 25, 2026, 11:00 PM