Files
claudemesh/apps/cli/tests/unit/sse-require-session.test.ts
Alejandro Gutiérrez 9075ae05df fix(cli): messages stop landing on the wrong sessions (wrong-peer noise, bug9)
A reply routinely fanned out to every session of the sender's member,
which is the "peers spread messages to the wrong peers" noise. Three
links fed each other:

- channel meta put the MEMBER key in from_pubkey/from_id, and the skill
  told the model to reply by from_name (repeatable across sessions).
  from_pubkey/from_id are now the sender's session key; the member key
  moves to from_member_pubkey; the skill says reply to from_pubkey.
- a DM to a member key was claimed by that member's daemon socket and
  shown to all its sessions. New daemon/resolve-target.ts is the single
  resolver for /v1/send: a member key maps to its only live session, is
  refused (409 member_target_ambiguous + candidates) when it has several,
  and needs --fanout to reach all of them on purpose. A duplicated
  display name or prefix is ambiguous instead of first-match; control-
  plane rows are never addressable.
- a session MCP whose token didn't resolve fell into the unfiltered SSE
  stream and rendered every sibling's DMs. The MCP now subscribes with
  scope=session and gets no message events without a resolved session.

Also: --self lists only the current mesh and requires a registered
session (it could message its own new row); a DM to a control-plane row
says so instead of the wrong "session pubkeys are ephemeral" hint, and
`peer list --all` tags those rows (bug9).

Spec: .artifacts/specs/2026-09-26-session-identity-and-routing.md §2-3, §6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:29:03 +01:00

24 lines
1.2 KiB
TypeScript

/** Spec 2026-09-26 §2: a session MCP whose token doesn't resolve gets no
* message events — it used to receive every sibling session's DMs. */
import { describe, expect, it } from "vitest";
import { shouldDeliver, type DaemonEvent } from "~/daemon/events.js";
const msg = (data: Record<string, unknown>): DaemonEvent => ({ kind: "message", ts: "", data });
describe("shouldDeliver: requireSession", () => {
it("an unresolved session MCP gets no messages", () => {
expect(shouldDeliver(msg({ recipient_kind: "session", recipient_pubkey: "ab" }), { requireSession: true })).toBe(false);
expect(shouldDeliver(msg({}), { requireSession: true })).toBe(false);
});
it("diagnostic subscribers (no filter) keep the full stream", () => {
expect(shouldDeliver(msg({ recipient_kind: "session", recipient_pubkey: "ab" }), {})).toBe(true);
});
it("a resolved session still gets its own messages only", () => {
const f = { requireSession: true, sessionPubkey: "ab" };
expect(shouldDeliver(msg({ recipient_kind: "session", recipient_pubkey: "ab" }), f)).toBe(true);
expect(shouldDeliver(msg({ recipient_kind: "session", recipient_pubkey: "cd" }), f)).toBe(false);
});
});