/** * Slack mention resolver for team channels. * * Agents are not Slack users, so a message like `@sam 看一下这个` arrives as * plain text. This module finds every `@name` token in a Slack message and * maps it to a Crewly agent session, and for names that match nothing it * offers "did you mean" suggestions (bounded Levenshtein distance) so the * bridge can answer a typo instead of silently ignoring it. * * Pure functions, no I/O — the bridge supplies the candidate list from the * team's members. * * @module services/slack/slack-mention-resolver */ /** One agent the resolver may match against. */ export interface MentionCandidate { /** Display name as configured on the team member (e.g. `Sam`). */ name: string; /** Agent session name (e.g. `crewly-alpha-sam`). */ sessionName: string; /** * The agent's own Slack bot user id when it has a real identity. A native * Slack mention arrives as `<@U…>` and is matched here first. */ botUserId?: string; } /** An `@name` that matched no candidate, with close alternatives. */ export interface UnknownMention { /** The token as typed, without the `@`. */ token: string; /** Candidate display names within the edit-distance cutoff, best first. */ suggestions: string[]; } /** Result of {@link resolveSlackMentions}. */ export interface ResolvedSlackMentions { /** Session names of matched agents, in first-mention order, deduped. */ mentions: string[]; /** Tokens that matched nobody. */ unknown: UnknownMention[]; } /** Options for {@link resolveSlackMentions}. */ export interface ResolveSlackMentionsOptions { /** Max edit distance for a suggestion (default from constants). */ maxDistance?: number; /** Max suggestions per unknown token (default from constants). */ maxSuggestions?: number; } /** * Extract the user ids of native `<@U…>` mentions, in order, deduped. * * @param text - Slack message text * @returns Slack user ids */ export declare function extractNativeMentionIds(text: string): string[]; /** * Levenshtein edit distance between two strings, compared case-insensitively. * * @param a - First string * @param b - Second string * @returns The minimum number of single-character edits to turn `a` into `b` */ export declare function levenshtein(a: string, b: string): number; /** * The aliases a candidate answers to: its display name, the name with * whitespace removed, its full session name, and the short session * segment (`crewly-alpha-sam` → `sam`, matching the orchestrator bridge's * existing `@name` rule). All lower-cased. * * @param candidate - The agent * @returns Distinct lower-case aliases */ export declare function candidateAliases(candidate: MentionCandidate): string[]; /** * Extract the raw `@name` tokens from a message, in order, deduped * case-insensitively, with Slack's reserved words removed. * * @param text - Slack message text * @returns Tokens without the leading `@` */ export declare function extractMentionTokens(text: string): string[]; /** * Map every `@name` in `text` to an agent session. * * Exact (case-insensitive) alias matches win. A token that matches no * alias is reported under `unknown` with up to `maxSuggestions` candidate * display names within `maxDistance` edits, closest first. * * @param text - Slack message text * @param candidates - The agents that can be mentioned (team members) * @param options - Distance and suggestion caps * @returns Matched sessions and unknown tokens * * @example * ```ts * resolveSlackMentions('@sam @lee 看一下', [ * { name: 'Sam', sessionName: 'crewly-alpha-sam' }, * { name: 'Leo', sessionName: 'crewly-alpha-leo' }, * ]); * // → { mentions: ['crewly-alpha-sam'], unknown: [{ token: 'lee', suggestions: ['Leo'] }] } * ``` */ export declare function resolveSlackMentions(text: string, candidates: MentionCandidate[], options?: ResolveSlackMentionsOptions): ResolvedSlackMentions; /** * The agent a message is plainly addressed to by opening with its name. * Owner's rule, 2026-10-02: naming an agent at the start of a message counts * as addressing it, even with no `@`. * * Only the start of the message counts, and matching ignores case; longer * names are tried first ("Steve Bot" before "Steve"). Right after the name * must come one of: * - address punctuation `, , : : 、 ! ! ? ?` (`Aria,帮我…`, `Aria, can you…`); * - a CJK character (`Aria帮我…`), also after spaces (`Aria 帮我…`); * - the end of the message. * Written `@Name` (an `@` Slack left as plain text) any word boundary will * do. A bare name followed by a space and a Latin word — "Tidy up the docs", * "Aria can you" — is ordinary prose and addresses nobody. * * @param text - Slack message text * @param candidates - Agents that can be addressed here (anything with a display name) * @returns The matching candidate, or null */ export declare function leadingNameMention(text: string, candidates: readonly T[]): T | null; //# sourceMappingURL=slack-mention-resolver.d.ts.map