Frontmatter
| id | 10147 |
| title | Mailbox: Message node schema + addMessage / listMessages / getMessage / markRead |
| state | Closed |
| labels | enhancementaiarchitecturecore |
| assignees | tobiu |
| createdAt | Apr 21, 2026, 11:29 AM |
| updatedAt | Apr 22, 2026, 2:00 PM |
| githubUrl | https://github.com/neomjs/neo/issues/10147 |
| author | tobiu |
| commentsCount | 0 |
| parentIssue | 10139 |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [x] 10149 Mailbox: extend memory-core healthcheck with inbox + outbox preview, [x] 10148 Mailbox: archiveMessage / deleteMessage + retracted-marker semantics |
| closedAt | Apr 21, 2026, 11:25 PM |
Mailbox: Message node schema + addMessage / listMessages / getMessage / markRead
tobiu added parent issue #10139 on Apr 21, 2026, 11:32 AM
tobiu marked this issue as blocking #10148 on Apr 21, 2026, 11:33 AM
tobiu marked this issue as blocking #10149 on Apr 21, 2026, 11:33 AM
tobiu marked this issue as blocking #10150 on Apr 21, 2026, 11:33 AM
tobiu cross-referenced by #9999 on Apr 21, 2026, 7:03 PM
tobiu cross-referenced by #10139 on Apr 21, 2026, 10:27 PM
tobiu cross-referenced by #10146 on Apr 21, 2026, 10:30 PM
tobiu assigned to @tobiu on Apr 21, 2026, 10:30 PM
tobiu cross-referenced by PR #10167 on Apr 21, 2026, 10:55 PM
tobiu cross-referenced by #10168 on Apr 21, 2026, 11:24 PM
tobiu cross-referenced by #10169 on Apr 21, 2026, 11:24 PM
tobiu closed this issue on Apr 21, 2026, 11:25 PM
tobiu cross-referenced by PR #10170 on Apr 22, 2026, 12:30 AM
tobiu cross-referenced by PR #10171 on Apr 22, 2026, 12:47 AM
Mailbox: Message node schema + addMessage / listMessages / getMessage / markRead
Context
Parent #10139 phase 1. After Memory + Session are nodes and AgentIdentity exists, Message becomes the fourth first-class artifact. This ticket implements its graph schema + core send/receive tools.
Scope-rebalance note (2026-04-21, session
d69ac7a0-9fe8-4416-b766-cd9edb8bee71): the original AC #5 required permission-check integration (caller-identity = SENT_BY on write; read scoped to tenant + permission edges). That integration depends on #10146's permission-edge types + middleware, which don't exist yet. To avoid circular PR dependency and scope-overlap with #10146's "cross-tenant validation test suite" (concern I flagged in my epic-review Stage 3 observation on #10016), the permission-integration AC is relocated to #10146. This ticket ships the Message schema + 4 core tools with server-stampedSENT_BY(fromRequestContextService.getAgentIdentityNodeId()) and tenant-scoped read filters onSENT_TO = caller, but without the explicit permission-edge check. That layer lands in #10146 atop this foundation.The Problem
Agents today leave handoff notes via
add_memoryconvention — probabilistic, semantic-retrieval-dependent, no explicit addressing. Replace with typed addressed messages.The Architectural Reality
RequestContextService.getAgentIdentityNodeId()now populated on both transports (per #10145 merge) —SENT_BYis always server-stamped from the resolved identity, never client-supplied.The Fix
Messagenode:{id, subject, bodyText, createdAt, readAt, archivedAt, priority: 'low'|'normal'|'high', retracted: boolean}SENT_BY(Message → AgentIdentity)— required, indexed, server-stampedSENT_TO(Message → AgentIdentity | Role | Broadcast)— required, indexed. Three addressing modes: direct identity, role (role:next-session,role:next-session-of-opus,role:librarian,human:<login>), broadcast (*)ORIGINATES_IN(Message → Session)— requiredRELATED_SESSION(Message → Session)— optional NREFERENCES_TICKET(Message → Issue)— optional NIN_REPLY_TO(Message → Message)+PART_OF_THREAD(Message → Thread)— threadingTAGGED_CONCEPT(Message → Concept)— auto-emitted via Gemma4addMessage({to, subject, body, originSessionId, relatedSessions?, relatedTickets?, inReplyTo?, priority?})listMessages({box: inbox|outbox|all, unreadOnly?, threadId?, from?, to?, limit, offset})getMessage(id)markRead(id)fromargument is preemptively rejected byAuthMiddleware(landed in #10145); authorship is derived from the RequestContext.Acceptance Criteria
TAGGED_CONCEPTedges emit via Gemma4 extractor for message bodiesSENT_BYalways matchesRequestContextService.getAgentIdentityNodeId()— tested via anti-spoof attemptOut of Scope
CAN_READ_INBOX_OF/CAN_READ_MEMORIES_OF/CAN_READ_SESSIONS_OF/CAN_REPLY_TOedge types and wire them into mailbox read paths as a follow-up PR on top of this one.Avoided Traps
markReadwith archive or delete. Rejected. Read ≠ processed ≠ gone. Three separate states, three operations.add_memory. Rejected per reshape of #10139.fromfield. Rejected —AuthMiddleware(shipped in #10145) rejects at thecallToolchoke-point. Author is always the resolved identity fromRequestContext.Related
ai/mcp/server/memory-core/, graph schemaOrigin Session ID:
71dc3cd8-d39d-48e1-ac62-e240ca67d1a5(ticket creation) Reshape Session ID:d69ac7a0-9fe8-4416-b766-cd9edb8bee71(scope rebalance 2026-04-21, Claude Opus 4.7 / Claude Code)