/** * core/session-match.ts — one place that decides which session a name means. * * There were three. `dispatch` compared PAI project names against session * labels exactly; `send_to_session` did a ranked substring search; PAILot was * handed an id and never resolved anything. Different rules, different edge * cases, each fixed on its own — so `task-bus` failed to match a session * named `Task Bus` in the first while the second would have found it, and * the miss did not fail, it spawned a duplicate tab with none of the context. * * Matching is ranked rather than first-past-the-post, and two properties are * load-bearing: * * SEPARATORS ARE NOT SIGNIFICANT. Aliases are written machine-style and * sessions are named by a human; hyphen, underscore and whitespace all fold. * * A CALLER'S PREFERENCE OUTRANKS EXACTNESS. `send_to_session` prefers a live * Claude session over a shell, and must: every ended session leaves a shell tab * behind, and addressing "Clickr" after that session ended once matched the * leftover shell sitting in ~/dev/ai/clickr — where the message was executed * rather than read. An exact match against a shell must lose to a fuzzy match * against something that can actually receive. */ export interface SessionCandidate { id: string; name: string; paiName?: string | null; } /** How a name was matched, weakest last. Reported so a fuzzy hit is visible. */ export type MatchKind = "exact" | "normalised" | "substring"; /** * Which session a caller is really in, given what it claims and where it is. * * A caller announces itself with `ITERM_SESSION_ID`, which is an environment * variable and therefore inherited, copied and outlived. When it names a pane * that no longer exists, acting on it is worse than failing: a name written * against a dead id lands in the store, matches no enumeration ever again, and * leaves the real pane anonymous — so the operator renames, sees nothing * change, and renames again. * * The tty is the corrective. It says where the process is attached now, iTerm * reports it per session, and the two can therefore be reconciled. A live claim * still wins, because a caller that knows its own id is the best evidence there * is; the tty only decides when the claim has already been proved false. */ export declare function resolveCallerSession(claimedId: string | undefined, callerTty: string | undefined, live: { id: string; tty?: string | null; }[]): string | undefined; /** The name a human would call this session. */ export declare function labelOf(s: SessionCandidate): string; /** * Fold the written forms of one name onto common ground. * * A terminal title is decorated by whoever drew it — a spinner glyph while the * session is busy, the process in brackets after it — and none of that is part * of the name. Leaving it in matters only when the label IS the title, which is * exactly the case that goes wrong: an unregistered session falls back to its * title, `✳ Example Project (node)` fails to equal `Example Project`, and the * caller concludes nothing is running and opens a second one. * * Stripping is confined to those two decorations. Anything more eager would * start folding real names together, and a wrong match here does not spawn a * duplicate — it delivers work to the wrong session. */ export declare function normaliseLabel(s: string): string; export interface MatchOptions { /** Which strategies to try. Default: exact and normalised, never substring. */ kinds?: MatchKind[]; /** * Extra score per session, dominating match quality. * * Use for "this one can actually receive a message". Returning 1 for a live * Claude session and 0 for a shell reproduces the ranking send_to_session * needs, where a substring hit on a live session beats an exact hit on a * dead tab. */ prefer?: (s: SessionCandidate) => number; } /** * Resolve any of `references` to a session. * * Substring is opt-in for a reason: a project called `sl` would otherwise match * every session whose title happens to contain those letters, and for dispatch * a wrong match spawns nothing and delivers to the wrong place instead. */ export declare function matchSession(references: string[], sessions: SessionCandidate[], opts?: MatchOptions): { session: SessionCandidate; label: string; kind: MatchKind; } | null; //# sourceMappingURL=session-match.d.ts.map