feat(chat): agents read and address members by their mention links

Merged
DA
6d ago
+1395-686

PR 2 of 3 in replacing chat handles. It is stacked on #4004. Coding agents and Luke now read and write chat members as the app's mention links instead of @handles. Per the developer's decision there is no fallback for @handle text already posted.

What changes

Agents read members as links

  • read_chat now names members by link: you, members[] ({ mention, kind, left? } replaces handle), each message's from, a DM's in, an ask's answeredBy, and a post's receipt (told/held/left).
  • unresolved holds the labels, as written, of links that name no member.
  • The new coder-host/chat-names.ts builds each member's link the way list_agents does (workspaceStepOf + writtenMention). A person's link uses their account name, an agent's its title or else its model, and Luke's is …/plan/<taskId>/luke.
  • A member no link can be built for gets a plain label (CHAT_MEMBER_LABEL): "Former agent", "Former member", or "Luke".
  • Message words reach agents as written. chatWordsForAgents, handlesIn, HandleOf and namesMember are deleted.

Agents address members by link

  • chatTargetSchema accepts #name, or a member link written whole ([@Name](url)) or as the bare URL. @handle is refused. CHAT_BOUNDS.MAX_TARGET_CHARS is 512, and the schema checks only the shape.
  • New chatTargetMember(target) in chat-mentions.ts.
  • New memberOfSubject(userId, organizationId, subject) in chat-store.ts. It wraps subjectMember with isWorkspaceMember, and subjectOf in chat-ask.ts is now exported.
  • These handle post_chat's to (dmWith), read_chat's channel (a DM is matched by what the other member stands for, not by spelling), and the window's to in workspace-chat.ts.
  • memberNamed, namedMember, findMemberByHandle, taskOfContext and findAgentTask are removed.

Only links notify

  • Removed: mentionedHandles, the MENTION regex, ChatMentions.handles, ChatPost.mentions, and the handle half of namedIn/placePost.
  • @luke typed as a word means nothing and mints no Luke.

Notices, senders, inbox entries

  • A notice's head is Chat · [@Name](link) in #x (m N):. chatNoticeWords now matches that head in full.
  • These are now display names, not links: notice metadata from, the inbox's accountChatEntry.from (in both the held read and the Neon live statement, which joins the author's account and agent), and an agent's LineSender.name.
  • The @ prefix is gone from chat-notice.tsx, agent-notices.ts and the transcript copy's heading.

One change beyond the brief: a chat notice no longer carries "Mentioned in this message" notes (steer.ts). Those notes are written as "The developer pointed you at…". Now that a notice carries member links, a teammate's message would otherwise be described as the developer pointing at people. This also drops notes for task or file links in chat messages. The servers a notice hands are unchanged.

Prompts: the coder's chat section tells the agent to name members by the links read_chat lists, and writes its task's Luke as [@Luke](<taskLink>/luke). "as @luke" is gone from Luke's prompt and the tool descriptions and refusals.

Kept for PR 3

The handle column is still minted and written (mintHandle, SPOKEN_FOR, LUKE_HANDLE, chatHandleSchema, MAX_HANDLE_CHARS), and the window's wire still sends handle. CHAT_TARGET_SIGIL.MEMBER is still used by the composer.

Tests

  • Handle-pinned tests are rewritten to links in: coding-agents-app.test.ts, coder-host.test.ts, brain-host-tools.test.ts, brain.test.ts, inbox-store.test.ts, chat-store.test.ts (the minting tests are kept), chat-wire.test.ts, chat-mentions.test.ts, and the desktop notice tests.
  • New tests cover:
    • post_chat to a member link, whole or bare, opens the DM
    • read_chat lists members and you as links
    • a link in the words tells a member, and one naming no member comes back unresolved by its label
    • @word text tells no one and mints no Luke
    • @handle as to or channel is refused
    • the window's DM goes by link
    • a notice names its sender by link and carries no mention notes
    • the coder prompt's Luke link decodes as a Luke mention
  • JSON Schema goldens were regenerated with LUKE_UPDATE_FIXTURES=1. The 7 changed fixtures differ only in chatTargetSchema's pattern and max length.
  • Run locally: per-package tsc --noEmit, oxlint, biome, knip, repository-checks.sh, and vitest across features, ui, host, panel/tasks, wire, apps, the database store models, and the service agents, live and inbox suites.

Made with Luke


Devin Review