/** * Agent-to-agent message envelope (crewly#1375). * * `send-message` writes another agent's text into the recipient's turn with * `POST /terminal//write {mode:"message"}`. Before this envelope the * recipient saw only the bare text: no sender, no reply route. On 2026-10-18 * Pia (Flopost) got two such messages from Atlas (Think Tank) while she was * answering the owner's Shaw Brothers video thread; her bare `reply` landed * in that owner thread ("the owner asked (no work item running)"), and her * second answer went there too as `reply-channel "@Atlas …"`, because she had * no other address for him. * * Now the route puts one short header in front of an agent's message: * * `[MESSAGE from Atlas (Think Tank) — a colleague, not the owner. Answer with * send-message --to think-tank-atlas-b4e166f6; do not post it in an owner thread.]` * * The header is text, so it survives every queue the message may wait in, * and the harness reads the sender back from it ({@link parseAgentMessageSender}) * to route a bare `reply` to that agent. * * The header also carries the sender's route — the owner message or work * item the sender is answering (message-route.ts) — so the destination of * the original request travels across every agent hop * (message-routing audit 2026-10-18). * * Pure helpers; the member lookup is injected. * * @module services/messaging/agent-message-envelope */ import { type MessageRoute } from '../orc/message-route.js'; /** Opening of the header. */ export declare const AGENT_MESSAGE_HEADER_PREFIX = "[MESSAGE from "; /** Who sent a message, as the recipient should read it. */ export interface AgentMessageSender { /** Sender's session (its reply address) */ session: string; /** Display name, when known */ name?: string; /** Team display name, when known */ team?: string; /** What the sender is answering (owner message / work item), carried to the recipient */ route?: MessageRoute; } /** * Whether a text already opens with a harness envelope. * * @param text - Message text * @returns True when it must not be wrapped again * * @example * ```typescript * hasHarnessEnvelope('[CHAT:abc] hi'); // true * hasHarnessEnvelope('Thanks. Two things.'); // false * ``` */ export declare function hasHarnessEnvelope(text: string): boolean; /** * The header line for a message from `sender`. * * @param sender - Who sent it * @returns One header line (no trailing newline) * * @example * ```typescript * formatAgentMessageHeader({ session: 'think-tank-atlas-b4e166f6', name: 'Atlas', team: 'Think Tank' }); * // '[MESSAGE from Atlas (Think Tank) — a colleague, not the owner. Answer with send-message --to think-tank-atlas-b4e166f6; do not post it in an owner thread.]' * ``` */ export declare function formatAgentMessageHeader(sender: AgentMessageSender): string; /** * The text with the sender's header in front, unless it already carries a * harness envelope. * * @param text - Message text * @param sender - Who sent it * @returns Text to deliver */ export declare function wrapAgentMessage(text: string, sender: AgentMessageSender): string; /** * The sender session a delivered text's header names. * * @param text - Exactly what was delivered * @returns Sender session, or undefined when the text has no agent header */ export declare function parseAgentMessageSender(text: string): string | undefined; /** Looks a session up as a team member. */ export type MemberLookup = (session: string) => Promise<{ member: { name?: string; }; team: { name?: string; }; } | null>; /** * Who a calling session is, when it is a team member. Harness services that * write with an agent badge (the WorkItem dispatcher, the worktree notifier, * …) are not members and get null — their notices are not wrapped. * * @param session - Caller session (from `X-Agent-Session`) * @param lookup - Member lookup * @returns The sender, or null */ export declare function describeAgentSender(session: string, lookup: MemberLookup): Promise; //# sourceMappingURL=agent-message-envelope.d.ts.map