LearnNewsExamplesServices
Frontmatter
id10147
titleMailbox: Message node schema + addMessage / listMessages / getMessage / markRead
stateClosed
labels
enhancementaiarchitecturecore
assigneestobiu
createdAtApr 21, 2026, 11:29 AM
updatedAtApr 22, 2026, 2:00 PM
githubUrlhttps://github.com/neomjs/neo/issues/10147
authortobiu
commentsCount0
parentIssue10139
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[x] 10149 Mailbox: extend memory-core healthcheck with inbox + outbox preview, [x] 10148 Mailbox: archiveMessage / deleteMessage + retracted-marker semantics
closedAtApr 21, 2026, 11:25 PM

Mailbox: Message node schema + addMessage / listMessages / getMessage / markRead

Closed v13.0.0/archive-v13-0-0-chunk-5 enhancementaiarchitecturecore
tobiu
tobiu commented on Apr 21, 2026, 11:29 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-stamped SENT_BY (from RequestContextService.getAgentIdentityNodeId()) and tenant-scoped read filters on SENT_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_memory convention — probabilistic, semantic-retrieval-dependent, no explicit addressing. Replace with typed addressed messages.

The Architectural Reality

  • Graph schema has Memory, Session, AgentIdentity nodes (prerequisites).
  • Existing graph tools absorb most access patterns; mailbox tools are thin wrappers with index hints.
  • SQLite FTS5 covers keyword body search; concept edges (#10030) cover semantic cheaply.
  • RequestContextService.getAgentIdentityNodeId() now populated on both transports (per #10145 merge) — SENT_BY is always server-stamped from the resolved identity, never client-supplied.

The Fix

  • Message node: {id, subject, bodyText, createdAt, readAt, archivedAt, priority: 'low'|'normal'|'high', retracted: boolean}
  • Edges:
    • SENT_BY(Message → AgentIdentity) — required, indexed, server-stamped
    • SENT_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) — required
    • RELATED_SESSION(Message → Session) — optional N
    • REFERENCES_TICKET(Message → Issue) — optional N
    • IN_REPLY_TO(Message → Message) + PART_OF_THREAD(Message → Thread) — threading
    • TAGGED_CONCEPT(Message → Concept) — auto-emitted via Gemma4
  • Tools (MCP on memory-core):
    • addMessage({to, subject, body, originSessionId, relatedSessions?, relatedTickets?, inReplyTo?, priority?})
    • listMessages({box: inbox|outbox|all, unreadOnly?, threadId?, from?, to?, limit, offset})
    • getMessage(id)
    • markRead(id)
  • Anti-spoof: from argument is preemptively rejected by AuthMiddleware (landed in #10145); authorship is derived from the RequestContext.

Acceptance Criteria

  • Message node type + edge types in graph schema
  • Four tools implemented, exposed via MCP, self-documenting descriptions
  • Playwright unit tests: send, list (inbox/outbox/filtered), get, markRead
  • Addressing modes tested: direct identity, role-based, broadcast
  • TAGGED_CONCEPT edges emit via Gemma4 extractor for message bodies
  • SENT_BY always matches RequestContextService.getAgentIdentityNodeId() — tested via anti-spoof attempt

Out of Scope

  • Lifecycle (archive/delete) — separate sub #10148
  • Healthcheck integration — separate sub #10149
  • Semantic "find related" — optional sub #10150
  • Cross-tenant permission-edge enforcement — relocated to #10146 per the scope-rebalance above. #10146 will add the CAN_READ_INBOX_OF / CAN_READ_MEMORIES_OF / CAN_READ_SESSIONS_OF / CAN_REPLY_TO edge types and wire them into mailbox read paths as a follow-up PR on top of this one.

Avoided Traps

  • Conflating markRead with archive or delete. Rejected. Read ≠ processed ≠ gone. Three separate states, three operations.
  • Nested message schema in add_memory. Rejected per reshape of #10139.
  • Client-supplied from field. Rejected — AuthMiddleware (shipped in #10145) rejects at the callTool choke-point. Author is always the resolved identity from RequestContext.

Related

  • Parent: #10139
  • Depends on: #10143 (Memory + Session nodes ✓), #10144 (AgentIdentity nodes ✓), #10145 (identity substrate + anti-spoof ✓ shipped)
  • Siblings: Lifecycle sub #10148, Healthcheck sub #10149, Semantic sub #10150
  • Follow-up: #10146 bolts cross-tenant permission-edge enforcement onto the mailbox primitives from this ticket
  • Files: ai/mcp/server/memory-core/, graph schema

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

tobiu added the enhancement label on Apr 21, 2026, 11:29 AM
tobiu added the ai label on Apr 21, 2026, 11:29 AM
tobiu added the architecture label on Apr 21, 2026, 11:29 AM
tobiu added the core label on Apr 21, 2026, 11:29 AM
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 referenced in commit e69cfb2 - "fix(ai): complete openapi schema and parameter signatures for mailbox (#10147)" on Apr 21, 2026, 11:13 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 referenced in commit 43d950e - "feat(ai): multi-tenant mailbox permissions and validation (#10146) (#10147) (#10167) on Apr 21, 2026, 11:25 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