/**
 * Prompt cleaning and topic extraction for session messages.
 *
 * Strips noise injected by the agent framework (system context lines, XML
 * wrapper tags, team-spawn boilerplate) so that only the real user intent
 * remains. Used by the session picker to show a human-readable topic line.
 */
import type { SessionEvent } from './types.js';
/**
 * True when a `role=user` message is harness-injected scaffolding, not a genuine
 * user turn. Text-based so it is cross-harness by construction (the markers are
 * the same wrappers across Claude/Codex/etc.); harnesses that already drop their
 * own scaffolding at parse time simply never reach here.
 */
export declare function isSyntheticUserMessage(raw: string | undefined): boolean;
/**
 * Inner text of a `<user_query>` wrapper, or the original string if none.
 * Same extraction Cursor already applies in `parseCursorUserText`.
 */
export declare function unwrapUserQuery(text: string): string;
/**
 * Return one genuine user turn in full.
 * Synthetic scaffolding is rejected as a whole so an injected Apps/AGENTS
 * preamble can never become the session's displayed request. Unlike the topic
 * cleaner, this intentionally preserves internal whitespace and every line of
 * the user's actual request. A Grok/Cursor `<user_query>` wrapper is unwrapped
 * so the stored turn is the originating request, not the harness tags.
 */
export declare function cleanFirstUserMessage(raw: string | undefined): string | undefined;
/** First genuine user message from an already-normalized event stream. */
export declare function firstUserMessageFromEvents(events: SessionEvent[]): string | undefined;
/**
 * Extract the invoked slash-command name from a raw `role=user` message, or
 * undefined when the message carries no `<command-name>` wrapper. Used by
 * `parseClaudeContent` to populate `SessionEvent.slashCommand`.
 *
 * Normalizes a leading `/` onto the result: current Claude Code wraps the
 * name WITH the slash already (`/recap`), but the wrapper predates that and
 * older transcripts carry the bare name (`recap`) — normalizing here means
 * every consumer sees one consistent `/name` shape regardless of vintage.
 */
export declare function extractSlashCommandName(raw: string | undefined): string | undefined;
/**
 * Extract the invoked slash-command name from the `SlashCommand` tool's input
 * — the MODEL invoking a command programmatically, not the user typing it.
 * The tool's `command` field carries the full invocation text (name plus any
 * args, e.g. `/code:commit fix the bug`); only the leading command token is
 * the name, matching {@link extractSlashCommandName}'s shape.
 */
export declare function extractSlashCommandFromToolInput(toolInput: Record<string, any> | undefined): string | undefined;
export declare const HEADLESS_PLAN_MODE_PREFIX = "You are running in HEADLESS PLAN MODE.";
/** Strip framework noise from a raw session prompt, returning only meaningful user text. */
export declare function cleanSessionPrompt(raw: string): string;
/**
 * How a session's first user turn reads once its framework noise is stripped —
 * so a UI row can show `[image]` / `$ cmd` / `/skill` instead of a screenshot
 * path, a pasted shell line, or a skill's install directory.
 */
export type UserPromptKind = 'text' | 'image' | 'command' | 'skill';
export interface ClassifiedPrompt {
    /**
     * The one-line, display-ready form of the prompt — never a bare path. NOT
     * length-capped: the recap card shows it in full; row consumers cap it
     * themselves (`deriveSessionRecap`).
     */
    clean: string;
    kind: UserPromptKind;
}
/**
 * Classify a raw first user turn into a display-ready `{ clean, kind }`, so a
 * Fleet/session row shows the user's actual intent rather than path noise:
 *
 *   - `skill`   — a `/skill` invocation's injected "Base directory for this
 *                 skill: …" line collapses to `/<name>`.
 *   - `command` — a `$ `-prefixed shell paste keeps only the first command.
 *   - `image`   — a screenshot/image path standing alone, or an image
 *                 attachment with no real accompanying text, becomes `[image]`.
 *   - `text`    — everything else: wrapper tags stripped (via
 *                 {@link cleanSessionPrompt}) and any inline image path replaced
 *                 with `[image]` so a mixed prompt still drops the path.
 *
 * Pure and cross-harness (text markers, no per-agent branch). `hasImageAttachment`
 * lets a caller that only has an already-extracted topic line still detect an
 * image-only turn from the row's attachment list.
 */
export declare function classifyUserPrompt(raw: string | undefined, opts?: {
    hasImageAttachment?: boolean;
}): ClassifiedPrompt;
/**
 * Collapse a harness-generated session title when it is injected skill
 * scaffolding; otherwise leave the title as the harness wrote it.
 *
 * Claude's `ai-title` and Cursor's `chatMeta.title` are both derived from the
 * first turn, so a session opened with a skill gets named after the injected
 * "Base directory for this skill: …" line. {@link classifyUserPrompt} reports
 * `kind: 'skill'` only for that line, so collapse it to `/<skill>`. Empty or
 * whitespace-only input yields `undefined` so the caller falls through to the
 * first-prompt topic.
 *
 * A user-authored title (Claude `/rename` / `custom-title`) is never passed
 * here — the caller keeps it verbatim.
 */
export declare function cleanGeneratedSessionLabel(title: string | undefined): string | undefined;
/** Extract a one-line topic from a raw user message, or undefined if the message is pure noise. */
export declare function extractSessionTopic(raw: string): string | undefined;
